I've released phunction a few days ago, and I believe it takes the procedural meaning to the limit. It evolved from this outdated core-only code. I encapsulated the functions in half a dozen classes to avoid name collisions and add some contextual meaning but other than that it should take less than 10 lines of changes to make it fully functional.
I tried to make phunction different from all the other frameworks out there by placing all the logic regarding a specific action in only one function, routing for instance (called in a OO-way):
ph()->Route('/pay/(:num)', null, 'payAmmount'); // num is passed to the function
ph()->Route('/hello/:any', null, 'functionHello'); // any is matched but not passed
ph()->Route('/', 'mainClass', 'index'); // OO controllers
No need to call stupid run()
methods.
I'm also quite proud of the DB()
function/method (phunction), this single function can connect to a MySQL / SQLite / PostgreSQL / Firebird database using the PDO drivers, after the connection is established it can run prepared queries, returns false on errors or:
lastInsertId()
on INSERT
/ REPLACE
statements
rowCount()
on UPDATE
/ DELETE
statements (number of changed rows)
fetchAll()
on SELECT
/ EXPLAIN
statements (an associative array with the result set)
An example:
//ph()->DB('./sqlite.db'); or
ph()->DB('mysql://localhost:3306/blog', 'user', 'pass');
$id = ph()->DB('INSERT INTO "users" (id, name, pass) VALUES (?, ?, ?);', null, 'alix', '***'); // last insert id
ph()->DB('DELETE FROM "users" WHERE id = ?;', $id); // # of changed rows
$users = ph()->DB('SELECT * FROM "users";'); print_r($users);
Other phunctions worth looking at are probably:
ph()->View()
// probably better than limonade-php
ph()->Disk->Image()
// crops, resizes, converts, displays and saves images
ph()->Net->Email()
// correctly sends emails (even i18n ones) using mail() or SMTP servers
ph()->Net->Paypal()
ph()->Text->Slug()
- others...
I'm struggling trying to find the time to write the documentation and design the website but the code is already available and it's pretty straightforward with semantic variables and method names. Should be fairly easy to pick up and hack away. Shameless ad incoming. =P
Your feedback is very welcome.
@mario: Adressing the points you on your question, within the phunction context:
Does it enforce some kind of input
filtering?
I'm not sure if you mean input validation of input sanitization but phunction sort of does both.
If you have register_globals = On
and you're running PHP 5.3.0 or higher the phunction constructor automatically strips all added slashes in both keys and values in the GPCR
superglobals, in a recursive, yet fast/clever manner (check the json_encode
/ json_decode
code). Bare in mind that phunction is fully compatible with PHP 5.2.0+, PHP 5.3 is only necessary to fix magic_quotes.
Besides dealing with magic_quotes
, the ph()->Input()
phunction can be used to validate and optionally prep/sanitize $_REQUEST
variables.
// phunction::Input definition
function Input($input, $filters = null, $callbacks = null, $required = true)
Some examples:
$name = ph()->Input('name', 'phunction_Is::Set', 'trim', true);
$email = ph()->Input('email', 'phunction_Is::Email', 'trim|strtolower', true);
$website = ph()->Input('website', 'phunction_Is::URL', 'trim', false);
Multiple validation or sanitization functions can be separated with |
.
Request routing features are cool, but
it's not the only interesting part in
frameworks.
Agreed. Routing is only the first step in a web application, a framework should provide generic methods that solve common problems, and that's what phunction aims for. Nonetheless, since routing is always necessary, it should be done the right way, with speed and flexibility in mind:
// phunction::Route definition
function Route($route, $class = null, $method = null, $on = null) {}
First of all the phunction::Route()
method doesn't need to pre-analyse routes and an after call to a Run()
method or similar. Each route is matched upon the call to the Route()
method - the first route that matches the URL is executed. This means that if you had 100 routes and the first or the second one matches the URL it wont need to match all the others. More:
- it is smart enough to match routes to URLs with or without forward slashes
/
and with or without mod_rewrite
configurations, even when you aren't calling ph()->Route()
from index.php
.
- provides the
:any
and :num
shorthands that match any characters or numbers, respectively.
- the
$on
argument can be used to match the HTTP request method, which is great if you're looking to build a REST API for instance.
- any matched regex capturing groups
(REGEX)
are passed to the $class::$method
or to $method
(in this case a function) as arguments
- if both
$class
and $method
aren't defined ph()->Route()
returns a boolean, this is great if you want to cascade routes in order to improve the speed even further, an example:
if (ph()->Route('/post/') === true)
{
ph()->Route('/post/add/', 'posts', 'add', 'POST');
ph()->Route('/post/view/(:num)', 'posts', 'view', 'GET');
ph()->Route('/post/delete/(:num)', 'posts', 'delete'); // match any HTTP method
ph()->Route('/post/update/(:num)', 'posts', 'update', 'POST');
}
If the requested URL doesn't start with [/yourFile.php]/post/
the four nested routes will never need to be executed, thus it'll be faster. The Route()
phunction also "caches" (using a static variable) the processed requested URL to be even more efficient.
The MVC pattern in particular doesn't
seem to be reliant on classes, and
might conceptionally look nicer in a
proper functions-only implementation.
I don't think MVC would look nicer without OOP, but you can do whatever you want. In phunction, Views should be simple .php
files:
// phunction::View definition
function View($view, $data = null, $return = false)
There is no templating overhead, I always though template engines like Smarty and similar are extremely stupid since PHP itself is a awesome template engine, for instance:
$data = array
(
'title' => 'Foods I Like...',
'foods' => array
(
'Pizza',
'Pasta al Dente',
'Hamburger',
),
);
ph()->View('/path/to/foods', $data);
/path/to/foods.php:
<?php ph()->View('/path/to/header', array('title' => $title)); ?>
<ul>
<?php foreach ($foods as $food): ?>
<li><?php echo $food; ?></li>
<?php endforeach; ?>
</ul>
<?php ph()->View('/path/to/footer'); ?>
/path/to/header.php:
<!DOCTYPE html>
<html>
<head>
<title><?php echo $title; ?></title>
</head>
<body>
/path/to/footer.php:
</body>
</html>
As you can see in /path/to/foods.php
it's possible to call views from within views (aka partials). Caching can also be done if you've the APC extension installed:
// get the view
$foodsView = ph()->APC('foodsView');
if ($foodsView === false)
{
// return the output instead of echoing it
$output = ph()->View('/path/to/foods', $data, true);
// cache the output for 60 seconds AND return it
$foodsView = ph()->APC('foodsView', $output, 60);
}
echo $foodsView;
As you can see it's pretty easy, and you can even write that in a more compact way:
if (($foodsView = ph()->APC('foodsView')) === false)
{
$foodsView = ph()->APC('foodsView', ph()->View('/path/to/foods', $data, true), 60);
}
echo $foodsView;
If APC is not available on your system it'll gracefully return the non-cached value.
Regarding Controllers, as you have seen above, they can either be methods of a class or simple functions - personally I prefer the OO way.
You can call a method of any OO Controller or Model from anywhere, by doing:
ph()->Object('/path/to/fooModel')->Method(); // or
ph()->Object('/path/to/fooController')->Method();
Calling non-OO Controllers and Models poses no problem in any framework from what I can tell.
The big PHP frameworks seem to
encourage mostly thin Models - which
are just DB access wrappers. So the
actual API doesn't seem a relevant
factor for a procedural framework.
I think it's the other way around: thin controllers, fat models. But I don't understand what you mean by "the actual API" - which API??
Pro HTTP features, like support
for conditional or partial requests,
Content-Negotiation?
Not at this time, maybe in the future.
For a thin framework, PHP-only
templates are probably the best
option(?). Or does it standardize some
CSS naming scheme?
As I said above, I advocate PHP-only templates, I believe they are the best option, and not just for thin frameworks. What I don't follow is how does this relate to CSS naming schemes...?
There must be an easier way / API to
output forms programmatically.
I thought about this for a while and I think there is no need to have PHP generate the HTML code dinamically if you use ph()->Value()
or ph()->Input()
with ph()->View()
correctly:
/path/to/formView.php:
<input type="email" value="<?php echo ph()->Value('POST', 'email', 'what to show by default'); ?>" />
If you really must you can use the ph()->Text->Tag()
function to aid you with the heavy lifting, but this will be less efficient that the solution described above.
Configuration requirements? Is there a
rigid script naming scheme?
In phunction there are no configurations whatsoever and only one convention must be followed. Whenever you call ph()->Object()
(either directly or via ph()->Route()
) the name of the class to instantiate must equal the basename of the filename (except the .php
extension, of course).
If it's a functional framework, does
it use a hooks system, where you can
use cascaded handlers/controllers or
views/templates.
I'm not a big fan of hooks, they add a lot of overhead since for every place you want to hook needs a condition like if (hooks exists for this section)...
, and I've never been in a situation where I needed to use hooks, so there is no support for them.
This doesn't mean that it isn't possible to use cascaded routes
/handlers
/controllers
/models
and views
(as I've shown above).
Or something weird, like output based
on a "ob_xmlfilter" and content
amending with phpQuery.
No. Please, nooooooooooooooooooooooooo.
Should be something contemporary, not
PHPLIB or PHP3 relicts.
phunction is compatible with PHP 5.2.0 and higher. I wanted to make it compatible with PHP 5.0, but it would make the code much bigger and PHP < 5.2 has a lot of bugs and security flaws anyway, so...