Ptero
Ptero is a fast and flexible model-view-controller framework for PHP. It allows you to generate models, views, controllers, and routes instantly over the command-line and automatically provides a strong, clean structure for running web apps.
Think wearing a giant robot exo-skeleton with lasers and missile launchers while crushing the puny challenges of archaic PHP in a two-hour-long feature film directed by Michael Bay. It's that good.
It has the good stuff
Features include:
- RESTful routing
- Sensible templates: layouts and views separate
- RoR-esque code generation
- PHP-ActiveRecord for ORM: No SQL needed!
- Installs dependencies quickly and easily with Composer
- Built in Ruby: Who doesn't love Ruby?
It takes care of the bad stuff
Don't worry about:
- PHP syntax errors
- copy-pasting or otherwise repeating code
- SQL, SQL injection, escaping strings, etc
- Confusing URLs and query strings
- Frustrating design or structural problems
How do I do it?
To start using Ptero, make sure your PHP command-line tool is up-to-date, then run the following:
$ gem install ptero
Now make an application:
$ ptero new Blog
There! Your web application is seeded and ready. Now you can start generating files.
$ cd Blog
$ ptero generate page hippo
$ ptero route /hippo Hippo
Now run an apache server with root directory Blog/www, and navigate to http://localhost/hippo to see a fully-formed page.
(I really should have a built-in server command, but making Ruby run PHP is a mess. If someone wants to write up a cross-platform WEBrick server for PHP, I would be glad to incorporate it)
Onward!
Now for the best part. Let's take a look at the generated code.
Here's the default controller, Blog/php/controllers/HippoController.php:
namespace Blog\Controllers;
class HippoController extends ApplicationController {
public function get() {
$this->render();
}
}
The controller inherits from ApplicationController, giving it all sorts of useful functionality, most notably the render() method.
Calling $this->render(); with (or without) an array mapping of arguments will automatically render the template of the same name as the controller, in this case Blog/views/hippo.html.twig.
Here it is:
{% extends 'application.html.twig' %}
{% block head %}
<!-- HTML head code produced by view: hippo -->
<script type="text/javascript" src="/assets/js/hippo.js"></script>
<link rel="stylesheet" type="text/css" src="/assets/css/hippo.css" />
{% endblock %}
{% block content %}
<!-- HTML content code produced by view: hippo -->
<h2>View: Hippo</h2>
{% endblock %}
This template is written in Twig. Click here to learn the basics of Twig.
Notice that the template has only a few lines of code, and that it extends a layout, called application.html.twig.
In this way, global layout changes can be made to the global application template, and individual changes made to individual pages.
For example, let's try doing some arbitrary server-side work and sending it to the client:
class HippoController extends ApplicationController {
public function get() {
$sum = 0;
for($i=1;$i<10;$i++) {
$sum += $i;
}
$this->render(array(
'sum' => $sum
));
}
}
the template:
{% extends 'application.html.twig' %}
{% block head %}
<!-- HTML head code produced by view: hippo -->
<script type="text/javascript" src="/assets/js/hippo.js"></script>
<link rel="stylesheet" type="text/css" src="/assets/css/hippo.css" />
{% endblock %}
{% block content %}
<!-- HTML content code produced by view: hippo -->
<h2>The sum is: {{sum}}</h2>
{% endblock %}
Now our code will print the $sum variable passed to it by the server.
Routing
We already did some routing with the $ptero route
command, but routes are also stored in a file and editable by hand. Let's open Blog/php/routes.php:
$application->route(array(
'/hippo' => 'Blog\Controllers\HippoController',
));
Look! The route we made is there! It maps the string '/hippo' to the controller class Blog\Controllers\HippoController.
You can also print all current routes by running
$ ptero routes
We can try using RESTful parameters in our application and passing them to controllers. Try this:
$ ptero generate page rhino
$ ptero route /rhino/:number Rhino
Now edit php/controllers/RhinoController.php:
namespace Blog\Controllers;
class RhinoController extends ApplicationController {
public function get($number) {
$number = intval($number);
$result = $number * $number;
$this->render(array(
'result' => $result
));
}
}
and make the template print our result:
{% block content %}
<!-- HTML content code produced by view: rhino -->
<h2>View: Rhino</h2>
<div>
Result: {{ result }}
</div>
{% endblock %}
Now navigate to http://localhost/rhino/74
Yay! Our application can compute squares!
Routes in the routes.php file support a full range of regular expressions, although stubs such as :number and :alpha can be useful. Learn more at https://github.com/anandkunal/ToroPHP
DATABASES!!!
Ptero wouldn't be complete without Databases, though, (as-of-yet) we have not incorporated the ability to generate them through the command-line tool.
You'll have to bring your own SQL server. Sorry. Try MAMP.
Ptero uses PHP-ActiveRecord Databases, which are as easy as pie to use with a few minutes' practice. Take a look.
The first step of using Ptero databases is to configure your application. Open config/config.php:
<?php
return array(
'templates_path' => 'views',
'controllers_path' => 'php/controllers',
'models_path' => 'php/models',
'database' => array(
'name' => 'database-name',
'user' => 'username',
'password' => 'password',
'server' => 'localhost'
)
)
?>
Change the values 'database-name', 'username', 'password', and 'server' to the configuration of your database so that PHP-ActiveRecord knows where to look.
Now let's generate another page. We'll assume that we have a table called Elephants in our database that has a primary-key int "id", a string "name", and another int "age."
$ ptero generate page elephant
$ ptero generate model elephant
$ ptero route /elephants/:number Elephant
In php/controllers/ElephantController.php:
<?php
namespace Blog\Controllers;
use \Blog\Models as DB;
class ElephantController extends ApplicationController {
public function get($id) {
$this->db_connect('development');
$id = intval($id);
$elephant = DB\Elephant::find($id);
$name = $elephant->name;
$age = $elephant->age;
$this->render(array(
'name' => $name,
'age' => $age
));
}
}
?>
In views/elephant.twig:
{% extends 'application.html.twig' %}
{% block head %}
<!-- HTML head code produced by view: elephant -->
<script type="text/javascript" src="/assets/js/elephant.js"></script>
<link rel="stylesheet" type="text/css" src="/assets/css/elephant.css" />
{% endblock %}
{% block content %}
<!-- HTML content code produced by view: elephant -->
<h2>View: Elephant</h2>
<h2>Name: {{ name }}</h2>
<h2>Age: {{ age }}</h2>
{% endblock %}
With this code in place, a request to http://localhost/elephants/n will output the information of elephant with id=n.
In this same way, you can create new Elephants and save them to the database by running something akin to:
$dumbo = new \Blog\Models\Elephant();
$dumbo->name = 'dumbo';
$dumbo->age = 25;
$dumbo->save();
In the post() method of a controller.
Inverse Commands
Each "create" command has an inverse command to undo it.
This "destroy" command will delete or undo a create operation.
USE WITH CARE ON UNCOMMITTED CHANGES.
Here are some common inverse commands:
$ ptero new Slideshow
$ cd Slideshow
$ ptero generate controller Index
$ ptero remove controller Index
$ ptero route /asdf/:alpha.jpg Tree
$ ptero unroute /asdf/:alpha.jpg
$ ptero destroy
Conclusion
Ptero is still in a phase of development, so please contact @LuminousRubyist, the creator and maintainer of this project, for any questions, comments, complaints, insults, or profound observations about everyday life.
Contributing
- Fork it ( https://github.com/luminousrubyist/ptero/fork )
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create a new Pull Request
Thank you
Thank you to all the libraries, and services that make this possible: