views:

789

answers:

8

I've stumbled across a couple of PHP Framework questions on SO. But like //so/questions/2648/what-php-framework it's mostly an itching on the surface or popularity contest. Skimming through each frameworks API or introduction docs didn't give me a good overview either.

However, I was wondering if there is something like a purely procedural PHP framework. That might make a more interesting comparision point to the big/popular object-structured frameworks. While of course this question comes mostly out of curiosity how a non-OO framework might look.

Features I'd examplary look out for (that seem amiss in the big PHP objectstyle frameworks) besides a procedural/functions-only coding style:

  • Does it enforce some kind of input filtering?
  • Request routing features are cool, but it's not the only interesting part in frameworks.
  • The MVC pattern in particular doesn't seem to be reliant on classes, and might conceptionally look nicer in a proper functions-only implementation.
  • The big PHP frameworks seem to encourage mostly thin Models, which are often just DB access wrappers. (Therefore an unimportant comparison point.)
  • Any pro HTTP features, like support for conditional or partial requests, Content-Negotiation?
  • For a thin framework, PHP-only templates are probably the best option(?). Or does it standardize some CSS naming scheme?
  • There must be an easier way / API to output forms programmatically.
  • Configuration requirements? Is there a rigid script naming scheme?
  • If it's a functional framework, does it use a hooks system, where you can use cascaded handlers/controllers or views/templates.
  • Or something weird, like output based on a "ob_xmlfilter" and content amending with phpQuery.
  • Should be something contemporary, not PHPLIB or PHP3 relicts.
+3  A: 

Although the framework itself is OO, Fat-Free Framework can be used as a procedural code library, but it only runs on PHP 5.3+.

stillstanding
Nice. A few examples come close to allow procedural handlers, even if the base library isn't. For the routing features the stringified commands look really clever. (But less so for the http methods.) While it's a pity it depends on 5.3 and the ugly namespace syntax, definitely worth to look into some more. Only the template feature doesn't look lightweight.
mario
It takes over 1MB just to render the welcome.htm page. You should really split some of that extra stuff into optional files to reduce load for simple pages. Nice work on some of the code though.
Xeoncross
+2  A: 

Found a functional PHP framework perchance:

http://www.limonade-php.net/

Looks really slim and easily understandable. But I most like the use of PHP as templating feature per default.

mario
+19  A: 

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

phunction PHP Framework


Dependency Map

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...

Alix Axel
Already sold on the logo typeface and 10KB download. It seriously lacks comments, but the interfaces seem rather useful. Even though it's not strictly procedural in itself, the jqueryish look and being able to still chain ordinary functions is nice. I especially like that it encourages prepared statements through the ->DB() interface. Alltogether it's a good collection of utility features, but should reuse some external libraries.
mario
@mario: Thanks, I appreciate it. I'll try to address the points on your question and comment latter on (probably not before the bounty ends though because I've national exams coming up). I'd just like to ask you three questions: **1)** why do you feel the need for hooks in a *procedural* PHP framework? **2)** What do you mean by an easier way / API to output forms automatically? Something that generates forms out of your database schemas? Helper functions that generate the HTML code and pre-fill the submitted data? **3)** What specific external libraries do you think phunction should [re]use?
Alix Axel
**1)** Not a requirement, but it's easier to do in procedural code than with OO, so I thought it might be an alternative concept to realize MVC. **2)** Not sure. But new Form(), new Input(), ->set_label() is cumbersome. Maybe something like a <form> string parser + augmenter, or a compound form_field($name, $type, $label, ...) API might be useful. **3)** That's optional, but I think code reuse often broadens applicability. For example the reuse of PHPs ZipArchive is nice, but PH might benefit from chaining to e.g.(?) PEAR::Archive to also support TAR archives (yes legacy, but you never know)
mario
Ok @mario, I'll take all that into consideration and I'll get back to you in a couple of days. ;)
Alix Axel
OMG - does it do Cornify???
Skilldrick
@Skilldrick: lol, I don't think so... What's that?
Alix Axel
@Skilldrick: +1 Ohh! Unicorns! xD
Alix Axel
@Alix - I thought it said 'Cornify' under 'Text', but maybe I'm misreading your writing. What does it say?
Skilldrick
@Skilldrick: Ahh, now it makes sense! It says `Comify`!
Alix Axel
I've just read the code - add commas - I get it!
Skilldrick
why not github :(
antpaw
@antpaw: Because last time I tried, Git was a pain in the ass to set up under Windows and GitHub doesn't offer website hosting like SourceForge does.
Alix Axel
@mario: I've updated my answer addressing your points. Regarding your comment, specifically the 3rd point, I don't think the framework would benefit from relying on 3rd party libraries since phunction is meant to be as portable as possible and PEAR is not always available in every configuration. By the way, the website and documentation for phunction probably won't be coming soon since I'm busy writing an OpenID phunction ATM and also some other stuff.
Alix Axel
@Alix: Sure, point 3 suggestion was optional anyway, and packaging PEAR libs within often just adds bloat. As for the json_encode/json_decode wrapper for magic_quotes, this really looks wacky. It's also not compatible with JSON reimplementations from Zend_Json or upgradephp and PHP_Compat. You should really go with a standardized solution: array_walk_recursive and stripslashes.
mario
This is probably the lengthiest answer on SO. Should get extra badges. **MVC Models**: It's best practice to use fat models and thin controllers. But it's my impression that the common PHP frameworks encourage quite the opposite. The documentation far too often concentrates on equating the database API and the Model concept. Seldomly do you read about $_REQUEST vars being part of the models, e.g. Exhibit CodeIgniter: http://codeigniter.com/user_guide/overview/mvc.html - "The Model represents your data structures. [...] model classes will [...] retrieve, insert [...] in your database."
mario
@Alix: I want to elaborate on the ob_xmlwhatever idea. Most current PHP frameworks rely on views. Boring. With PHP-only templates (which I also deem the most professional option), you often run the risk of outputting unencoded content. So I thought, it would be cool if instead of shred-wise printing HTML, if we could build our output as DOM tree. Instead of printing variables, why not $p().html($varname). Slower, but safer and a better abstraction. - But I see the irony of mentioning this in a question about procedural code, when it would be more fitting in really object-oriented frameworks.
mario
@mario: Thanks for the tip on upgradephp, didn't knew about that project. PHP_Compat doesn't seem to provide any json implementation. As for the Zend_Json and upgradephp implementations none of them have the constants introduced in PHP 5.3.0, show they shouldn't be compatible either way. Regarding `array_walk_recursive` it's a really bogus implementation, since it won't deal with keys and I'm not sure if it deals correctly with nested values. PHP_Compat provides a good implementation in `magic_quotes_gpc_off.php` but it's kinda big (and slow).
Alix Axel
@mario: What MVC should mean in the web is not clearly defined. My interpretation is that data should be handled in the Model, this also means validation, sanitization and prepping of the data. The Controller role should only be to pull/push data from and to the controller and display the Views accordingly. It seems to me that CodeIgniter thinks of models as a CRUD wrappers, leaving the handling of the data to the controller. There are lots of questions (and different answers) here on SO regarding this Model vs Controller dilemma.
Alix Axel
@mario: **No. Please, no! =)** Building a X(HT)ML output like that would be **extremely** slow, and not necessarily safer (think inline Javascript). Fat-Free is a good example how this kind of views (with sanitization, etc...) can be a pain in the ass - if you look at their bug reports most of them are view-related, not to mention the performance hit. The risk of outputting unencoded content is there, but we as programmers should know when to use `htmlspecialchars`. Making the framework guess is slow and can produce results that are hard to fix.
Alix Axel
@Alix (Cygwin + git) or msysgit for the lighter option both work well under Windows. But that's another question...
Skilldrick
@Alix: Very slow, true. But this is caused by PHPs addon XML support. Have a look at phpreboot, which makes DOM trees a built-in syntax and language feature, and so much more natural. But of course with phpQuery etc. it's at the moment seldomly practical or sensible to assemble output like that. But anyway, a framework should provide some means to marshall output in its templates. I've tried a static template pre-"compiler" with regexes to automatically add htmlentities() for any used $vars. Because I'd rather not have any htmlspec() references within the application logic.
mario
Really use array_walk() or _recursive. Don't care about keys. They contain special characters quite seldomly, and quite seldomlier progress into the database anyway. If any application contains references to $_GET["name'n'so"], there are more inherent problems to expect. Don't parent weird edge cases.
mario
@mario: Wouldn't a simple `foreach ($data as $key => $value) { $data[$key] = htmlentities($value); }` before `extract($data);` solve the problem?
Alix Axel
@Alix: I've used `extract(array_map("htmlentities",get_defined_vars()))` due to lack of a compound var. It's okay. But a "pre-compiler" can weed out $raw_ and $html_ vars, which should not be escaped.
mario
+3  A: 

You could arguably call Drupal a framework. They use a strictly procedural style. One of the curious things about it is the reliance on hook-functions for gluing the pieces together. This is a pattern seen in other non-oo software projects (For example in the Emacs editor).

troelskn
Skimmed through Drupal once or twice. It really has very practical interfaces, and would absolutely match the question. That hooks style api is what I mentioned. But objectively it's really too encyclopedical to count into "frameworks".
mario
A: 

If you want URL mapping and a minimalist framework look at extending http://twitto.org/

Xeoncross
Obviously it's a fun framework and security-wise needs rework. But it's an excellent KISS showcase. - Why a 10MB framework(?) when three lines suffice for a routing feature!
mario
+1  A: 

I will answer this using the semi-procedural open-source balPHP framework http://j.mp/9RMQ8u - the procedural part, with detailed responses to your points.

  • Does it enforce some kind of input filtering?

Not sure what you mean, perhaps ob_start, ob_get_contents?

  • Request routing features are cool, but it's not the only interesting part in frameworks. The MVC pattern in particular doesn't seem to be reliant on classes, and might conceptionally look nicer in a proper functions-only implementation.

Sure you can do MVC without a OO framework, although from experience it is painful. You have a file that contains all the functions for your Model, another file(s) for your View, and another file for your Controller functions. It just does not scale well when you have to start abstracting your code. Having a model that is not OO, you will quickly start pulling your hair out when you realise wow all my models could inherit from one class that contains save, fetch, and delete functions - rather than manually calling those functions and passing every darn param over to them each time! And trying to update every occurence of those functions when a param changes! Argh! To think of the headache maintenance would be.

In terms of MVC, it is the same issue. Maintenance would be a pain, your code would be too scattered. You are making a timebomb using procedural code.

  • 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.

Actually, "Fat Model, Skinny Controller" (google it) is the the general rule with professional frameworks. This allows less dependence on the framework you chose, and less dependency on your interface and architecture. You can pick up your models and drop them in, if you decide to use Symfony vs Zend Framework rather easily.

  • Any pro HTTP features, like support for conditional or partial requests, Content-Negotiation?

Not sure what you mean here, but perhaps this is what you're after? http://j.mp/9yYHEs

  • For a thin framework, PHP-only templates are probably the best option(?). Or does it standardize some CSS naming scheme?

PHP only templates are generally regarded the way to go. You can read all about how bad templating languages are here: http://j.mp/cvCCRa

  • There must be an easier way / API to output forms programmatically.

Yep. Google "Zend_Form" http://j.mp/9t57Xa, or "Generating forms from Doctrine ORM".

  • Configuration requirements? Is there a rigid script naming scheme?

Generally OO frameworks follow the PEAR naming scheme. The balPHP library follows: http://manual.b2evolution.net/Standard_Coding_Guidelines

  • If it's a functional framework, does it use a hooks system, where you can use cascaded handlers/controllers or views/templates.

Generally they are Ad-Hoc. I believe it's either http://cakephp.org/ or http://codeigniter.com/ which are procedural based, but never used them so not sure.

It seems more that your questions are aimed at avoiding OO, when really OO is a natural progression and evolution from procedural programming. Eventually you realise if you are doing anything that could be re-factored and grouped together, bang welcome to OO land - time to get cracking and learn it and use it! But of course, some systems you don't need OO and can just do a few liner procedural script.

balupton
+1 Nice utility functions, even though it's not a procedural framework in itself. / Input filtering = filter API for $_REQUEST etc / MVC without OO = I think you're wrongly equating procedural code with intermingled include scripts. And pure DB models are as simple in procedural code as in object-structured. But I'd personaly prefer the OOP notation for this part anyway. / Fat models and thin controlers = Yes that would be best practice, but it's not what most PHP frameworks seem to advocate. / Zend_Forms is the kind of overly verbose API I didn't like. Not sure if adding Doctrine helps there.
mario
Yeah good points. I hate Zend_Forms too, powerful but overly verbose, Doctrine helps as you can create zend forms directly from your Models which are defined nicely using YAML, with a few lines of code saving the uglyness.
balupton
+1  A: 

I'd recommend Caffeine framework, it can be used with using only functions and is under 10k zipped.

dalton
+1 Very basic, but likable and especially simple to follow the structure.
mario
+1  A: 

This response won't fit in the space reserved for comments. So let me do it here as I get into the nitty gritty of the issues raised.

Does it enforce some kind of input filtering?

Enforcing is a strong word. Once a framework starts meddling with data, the natural tendency for a framework is to become overbearing and enforcing other rules on the user/developer (and that you will see almost everywhere). Then it cannot become a framework for "the masses". Influencing or guiding a framework to follow your rules is more tasteful. A user following a framework's rules brings delight to some, but breeds disgust to others, specially if there are inconsistent and contradictory rules.

The MVC pattern in particular doesn't seem to be reliant on classes, and might conceptionally look nicer in a proper functions-only implementation.

MVC is not prescriptive. It is descriptive. Architects dictate the design. Engineers dictate the materials. MVC is a pattern, so implementation can vary significantly - even at machine language level.

There must be an easier way / API to output forms programmatically.

Here we have the Web designer back to becoming a second class citizen. So where's the separation being promoted by MVC again?

Configuration requirements? Is there a rigid script naming scheme?

Look for a framework that has a configuration-over-convention philosophy. There aren't that many around.

stillstanding