My largest criticism of Symphony (both version), and most frameworks built on the Zend Toolset (which I refuse to call a framework anymore than I’d call a stack of lumber the framing of a house) such as Magento is that they end up creating their own little programming meta languages in either xml, yaml, or the like.
Consider this excerpt from Magento’s app/design/frontend/base/default/layout/page.xml file
-->
<layout version="0.1.0">
<!--
Default layout, loads most of the pages
-->
<default translate="label" module="page">
<label>All Pages</label>
<block type="page/html" name="root" output="toHtml" template="page/3columns.phtml">
<block type="page/html_head" name="head" as="head">
<action method="addJs"><script>prototype/prototype.js</script></action>
<action method="addJs"><script>lib/ccard.js</script></action>
<action method="addJs"><script>prototype/validation.js</script></action>
<action method="addJs"><script>scriptaculous/builder.js</script></action>
<action method="addJs"><script>scriptaculous/effects.js</script></action>
<action method="addJs"><script>scriptaculous/dragdrop.js</script></action>
<action method="addJs"><script>scriptaculous/controls.js</script></action>
<action method="addJs"><script>scriptaculous/slider.js</script></action>
<action method="addJs"><script>varien/js.js</script></action>
<action method="addJs"><script>varien/form.js</script></action>
<action method="addJs"><script>varien/menu.js</script></action>
<action method="addJs"><script>mage/translate.js</script></action>
<action method="addJs"><script>mage/cookies.js</script></action>
<block type="page/js_cookie" name="js_cookies" template="page/js/cookie.phtml"/>
<action method="addCss"><stylesheet>css/styles.css</stylesheet></action>
<action method="addItem"><type>skin_css</type><name>css/styles-ie.css</name><params/><if>lt IE 8</if></action>
<action method="addCss"><stylesheet>css/widgets.css</stylesheet></action>
<action method="addCss"><stylesheet>css/print.css</stylesheet><params>media="print"</params></action>
<action method="addItem"><type>js</type><name>lib/ds-sleight.js</name><params/><if>lt IE 7</if></action>
<action method="addItem"><type>skin_js</type><name>js/ie6.js</name><params/><if>lt IE 7</if></action>
</block>
<block type="core/text_list" name="after_body_start" as="after_body_start" translate="label">
<label>Page Top</label>
</block>
<block type="page/html_notices" name="global_notices" as="global_notices" template="page/html/notices.phtml" />
<block type="page/html_header" name="header" as="header">
<block type="page/template_links" name="top.links" as="topLinks"/>
<block type="page/switch" name="store_language" as="store_language" template="page/switch/languages.phtml"/>
<block type="core/text_list" name="top.menu" as="topMenu" translate="label">
<label>Navigation Bar</label>
<block type="page/html_topmenu" name="catalog.topnav" template="page/html/topmenu.phtml"/>
</block>
<block type="page/html_wrapper" name="top.container" as="topContainer" translate="label">
<label>Page Header</label>
<action method="setElementClass"><value>top-container</value></action>
</block>
</block>
...
That should be enough to get my point across. This “config” file has become a programming language in it’s own right. It’s about as foolish in my eyes as smarty. And yet, at the same time, I’m having difficulty coming up with an alternative. I understand the need to abstract things - but there’s got to be a better way. Aside from the code bloat, I’m wanting to build an instructional framework to demonstrate principles and handle small tasks. That is NOT what we have here.
(Disclaimer - I like Magento. But it could seriously need some DRY principles applied).
So I’ve been working with my framework bootloader and front controller. The routing schema is pretty simple. The htdocs directory is kept empty except for a landing.php file. Later I intend to develop a way to drop cache files into there and start from those files instantiating as little of the framework as possible. Meanwhile a “map” directory holds the page controller load files.
These files aren’t classes - they are proceedures meant to execute in the scope of the main dispatcher. They are loaded in this method.
protected function parsePage( $route ) {
ob_start();
if ( $route instanceof Route ) {
require $route->path;
} else {
require( $route );
}
return ob_get_clean();
}
The cache files will pass a path to themself to this function so $route could be that string or an object that contains two values, the path, and the path after. As you can see, it’s the only variable in scope when the require occurs. Let’s look at what I have for that file.
<?php
namespace PNL;
if ($route->pathAfter) {
throw new FileNotFoundException();
}
$this->queue('layout');
//$response = $this->templates->fetch('testing');
//$response['title'] = "Hello World";
//$response['message'] = "This is a triumph. I'm making a note here, huge success.";
// echo $response;
The commented out section was a test that we can fetch a template, bind some data, and echo it out. I feel I need to be able to do this without classes before adding the classes into the mix. We are in an object buffer, so anything that goes out will be caught for postload processing. Since this index doesn’t support SEO rewrites it might catch, the check on path after is required to determine if we throw out to 404. The exception handlers don’t even use this loop. They get this rather elegant block to handle their output.
public static function exception( \\Exception $e) {
try {
print( Core::start()->templates->fetch(
$e->template ? 'exception.'.$e->template : 'exception.default',
$e->toArray()
)
);
Core::stop();
} catch ( \\Exception $er ) {
if (!headers_sent()) {
header("Content-Type: text/plain;");
header("HTTP/1.0 500 Internal Server Error");
header('X-Generator: PNL');
}
print("PNL Framework has encountered an error from which it cannot recover\
");
print("gracefully. Script terminated without proper closure. Exception Objects Follow\
");
print("\
INITIAL FATAL EXCEPTION\
\
");
print_r($e);
print("\
FINAL FATAL EXCEPTION\
\
");
print_r($er);
exit;
}
return false;
}
In brief the above error handler first tries to use the normal template engine to do a pretty report of the error. If a new error gets thrown then it dies with what amounts to a BSOD.
What I’m trying to come up with is a layout, blocks loader pattern which will be the normal class load pattern. Although these files can, maybe sometimes will, just load a template and shoot it out, eventually I want to be able to pull on a reusuable block hive. These landing files do what the xml of Magento or yaml files of Symphony do, without introducing a new syntax into the mix. To do that though I have to choose my function and class names carefully, and I will be employing chaining.