PHP 5.3 Namespace Support
Overview
Before we get too far let's answer the obvious question, Why would you want to use namespaces? Namespaces aim to avoid scoping problems in large PHP libraries and applications. Before the release of PHP 5.3, the work around for a lack of namespaces in PHP has been to Use_Very_Large_Class_Names. While that worked, typing that mess out was a pain and it still didn't guarantee you wouldn't have collisions. Now before you run off to do a Propel build enabling namespace support you should first read the section in the PHP manual on namespaces.
How Namespace Support Works
For the inquisitive minded folks you may be wondering how is it we are able to optionally support namespaces. The trick is the Propel runtime will call on generated classes with the appropriate namespace as given in the
=== Performing Propel Builds with Namespace Support===
Use of namespaces in Propel in entirely optional leaving the decision whether your project should use them to you. To enable the use of namespaces you simply need an entry similar to the following in your Propel properties file:
propel.namespace.enabled = true
Once you do that you should configure the namespaces you'd like to assign to the generated classes for your project. Out-of-the-box you have the ability to set five namespaces:
- propel.namespace.base - This is the base namespace. To get up and running with namespace support as quickly as possible you need only to set this one setting since, by default, all other namespaces are built on top of it. The default setting is the name of your project which should work just fine as long as your project name doesn't contain spaces or other special characters not allowed by PHP:
propel.namespace.base = ${propel.project} - propel.namespace.om - This is the namespace assigned to the Propel generated model classes. It should be noted this setting is applied to the om stubs as well as the base classes. The default setting is the propel.namespace.base value plus "Model":
propel.namespace.om = ${propel.namespace.base}::Model - propel.namespace.peer - This is the namespace assigned to the Propel generated peer classes. Just like the om namespace this setting is applied to the peer stubs as well as the base peers. The default setting is the propel.namespace.base value plus "Peer":
propel.namespace.peer = ${propel.namespace.base}::Peer - propel.namespace.map - This is the namespace assigned to the Map Builders generated by Propel. The default setting is the propel.namespace.base value plu "Map":
propel.namespace.map = ${propel.project}::Map - propel.namespace.nest - This is the namespace assigned to nested Propel objects. Because they are nothing more than specialized models their default setting use the same value as propel.namespace.om:
propel.namespace.nest = ${propel.namespace.om}
Obviously you have the ability to override th above settings but, for most people, the only setting you'd likely ever change would be the propel.namespace.base setting. Once you have those settings you can simply perform your Propel builds as you normally would.
Configuring Namespace Support in Propel Runtime
At the time of writing this you have to manually enable namespace support by editing your runtime configuration file (e.g. runtiem-conf.xml)
<namespaces enabled="true">
<namespace id="base" value="MyProject" />
<namespace id="om" value="MyProject::Model" />
<namespace id="peer" value="MyProject::Peer" />
<namespace id="map" value="MyProject::Map" />
<namespace id="nest" value="MyProject::Model" />
</namespaces>
Before the stable release of namespace support in Propel (slated for version 1.4) we expect these values will be pulled automatically from your generator property setting. For now you will need to add something like the above to your runtime XML and then re-run the convert-conf target.
When you run convert-conf you should have the following snippet in your runtime PHP array configuration:
'namespaces' =>
array (
'base' =>
array (
'value' => 'MyProject',
),
'om' =>
array (
'value' => 'MyProject::Model',
),
'peer' =>
array (
'value' => 'MyProject::Peer',
),
'map' =>
array (
'value' => 'MyProject::Map',
),
'nest' =>
array (
'value' => 'MyProject::Model',
),
'enabled' => true,
),
Example of Using Namespace-enabled Objects
Assuming you get the build done and the runtime settings are all to your liking you are likely to wonder, "Now what?". While the PHP manual does a pretty good job showing how you could use them a concrete example would likley save you some time:
<?php // Any references to classes without an explicit namespace will use this namespace MyProject::Commands; // Grab account model and peer (generated by Propel) require_once getOption('path_models') . 'Account.php'; require_once getOption('path_models') . 'AccountPeer.php'; // Alias all Propel generated OM objects as "Model" use MyProject::Model as Model; use MyProject::Peer as Peer; // Logger require_once getOption('path_framework'). 'Log.php'; // Alias framework classes as FM use MyProject::Framework as FM; class LoginCommand extends BaseCommand { public function execute() { // filter and validate input try { $sanitizedData = $this->filterAndValidateInput(); // :: is the global namespace which all Propel runtime classes exist in $query = new ::Criteria(); $query->add(Peer::AuthorPeer::EMAIL, $sanitizedData['email']); $query->add(Peer::AuthorPeer::PASSWORD, $sanitizedData['password'] $accounts = Peer::AuthorPeer::doSelect($query); if (count($accounts) <> 1) { FM::Log::log('Failed login attempt for email: ' . $sanitizedData['email']); throw new Exception('Invalid email and/or password'); } // NOTE we overrode default Propel setter to assume current date/time if none was given $accounts[0]->setLastLogin(); $accounts[0]->save(); $_SESSION['user'] = $accounts[0]; return; } catch (Exception $e) { FM::Log::log('Validation failed: ' . $e->getMessage()); throw $e; } } protected function filterAndValidateInput($filter_args = null) { // You are filtering *all* user input, right! ;-) // POST has: array('email'=>'jdoe', 'password'=>'J@neD0e'); $filter_args = array('email'=>FILTER_VALIDATE_EMAIL,'password'=>FILTER_SANITIZE_ENCODED); // Have parent do heavy lifting here. NOTE it is going to filter against POST by default. return parent::filterAndValidateInput($filter_args); } } ?>
