We released a new version of the Web:Extend framework today. The work continue towards the 1.0 release.
The current version fixes a number of issues and makes various improvements. The API didn’t change much, although we did a few additions:
Forms externals:
Forms externals are a mechanism allowing a developer to build part of a form dynamically. Currently it allows a developer to retrieve options for selectable widgets directly from the model domain just by specifying a function or method to call. For example you could retrieve a list of users from a users set using <external type="set" source="myUsersSet::fetchFormOptions"/>. This method would return value/label pairs to be used as option items. It can also be used for hierarchical data types, where the hierarchy is represented by a multi-level array.
API browser:
The new API browser is based on the jQuery Finder plugin. There is still some quirks here and there, and we also intend to add additional functionality with subsequent releases (like a search function, allowing you to find the deprecated API, or all the interfaces, for example), but the main functionality is there and the result is already better than the original. Feel free to comment on it and suggest how we could improve it.
New server:
Two weeks ago we moved to a new and much bigger server. Now that we have enough flexibility we intend to put various services, including daily test reports from various sources (anyone will be able to setup a daily test that will be aggregated and displayed on our server). We’ll also start a few projects that will demonstrate how to use the framework. More information on that later.
Next release:
Our work will continue in the next release. It will include plugins, since after some discussion we now have a better idea of how we want to implement that. It will also include various improvements, most of them detailed in the roadmap.
Thanks for your interest and see you soon!
March 24th, 2009 by Loïc Hoguin · Tags: PHP5, Release, Web:Extend · No Comments
Hello folks,
Today we released the next version of the Web:Extend framework, 0.4 beta.
This release introduces many many things. We improved the rendering pipeline; added an LDAP module (this is still a work in progress); improved the HTTP classes (cookies, sessions, file uploads); added QUnit, jQuery’s unit testing framework, to test these HTTP classes; and last but not least, added the UI module.
The UI module is our answer to RAD development. It works side by side with the model scaffolding classes to allow you to quickly design an application. It contains useful components like CRUD and pagination, that you only have to configure and render in your templates.
You can download the source code at sourceforge. It is licensed under the LGPL.
If you have some time to spend, head over to the documentation and leave us a comment, as we will use your input to improve the framework.
Next month we will release the Cache module along with a Plugins system, among other things. You might want to come back here in a week to discuss the RFC about these two additions.
Thanks for your interest.
February 14th, 2009 by Loïc Hoguin · Tags: PHP5, Release, Web:Extend · No Comments
We’re proud to announce today the first beta release of the Web:Extend framework. This release is an important milestone because it marks significant progress since the project was started about three years ago. It’s also been almost a year since the last alpha release and a lot has changed.
The project is expected to stay in beta stage for at least a few months. At this time it is not stable enough for production, but it is stable enough to begin to learn about it and even to use it in small personal projects you might have. Most of the changes planned for the next releases do not change the API, with the exception of one or two function name changes. And although it’s a beta release, about 75% of the code is estimated to be covered by unit tests – and this will only improve with each new beta releases.
The documentation is becoming pretty good. A few chapters are still missing but they will be written shortly. All the essential chapters are available so learning the basics shouldn’t be a problem. A tutorial is even available (more will follow). You can also use the API viewer for fast lookup of classes or functions; once it’s loaded it runs locally.
This release introduces a few important features.
This last item is pretty exciting, especially if you pair it with the upcoming UI Module, planned for the next release, 0.4 beta. It is scheduled to be released in about a month. We follow a 5-weeks per release cycle.
You can download the source code at sourceforge. It is licensed under the LGPL.
If you wish to help us, there is a lot you can do. The most important thing is to proof-read the documentation or the API and to forward us any problem you might have with it. Typos, invalid code, comprehension problems or anything else. You can also discuss the various RFC or try to learn how to use the framework and report bugs you may find. At a later time you will also be able to translate it in your language.
Please leave us any comment you might find relevant, as we will use your input to improve the framework.
Thanks for your interest.
January 15th, 2009 by Loïc Hoguin · Tags: PHP5, Release, Web:Extend · No Comments
About two months ago, Rasmus Lerdorf gave a talk at FrOSCon entitled Simple is Hard. If you don’t know about it yet I recommend reading the slides before continuing with this article.
Rasmus proved how hard it is to stay simple when writing an application or a library, and explained that the main victims of complexity are scalability, performance and security. He used a good number of frameworks to prove his point, showing the overhead in performance for an “Hello world!” application along with the graph of all the included files for generating such a simple request. Of course your application will be more than just an “Hello world!” but this shows just how much is loaded by the various frameworks for doing such a simple task.
I found the results to be frightening. So I looked at how simple our framework is compared to the others.
After firing siege, I got a very bad result: only around 120 trans/sec when PHP alone did more than 4000 trans/sec. It wasn’t surprising however, considering no performance optimization was ever done in the project. With the project reaching beta soon, it’s time to change this and multiply the speed.
You can take a look at the inclued file from that day:
Firing up the XDebug profiler, we could observe this call graph:
It’s easy to see what takes time… The weeAutoload::addPath method takes 76% of the execution time, and the configuration file loading 12%. Both can be cached and are only subject to change in a development environment or when upgrading, so we can automatically cache them when DEBUG mode is off and ignore the cache otherwise.
The cache method used is pretty simple. The cache files have no expiration time, they’re just deleted when upgrading an installation to a new version. If something goes wrong, the global constant NO_CACHE will happily ignore the cache files. And in development, cache is ignored as long as you have DEBUG enabled, which you should have all the time when developing.
The first time the script runs the data (an array in both case) is written to the cache file as valid PHP using var_export. Subsequent runs will simply require this file to load the cache. To prevent security problems, the file mode is set to 0600, disabling its access to anyone other than the webserver’s user.
Using siege today I get these results:
essen@karen (0) % siege -c 5 "http://localhost/wee/trunk/" -b -t30s > /tmp/wee-today ** SIEGE 2.66 ** Preparing 5 concurrent users for battle. The server is now under siege... Lifting the server siege... done. Transactions: 40995 hits Availability: 100.00 % Elapsed time: 30.16 secs Data transferred: 3.44 MB Response time: 0.00 secs Transaction rate: 1359.25 trans/sec Throughput: 0.11 MB/sec Concurrency: 4.97 Successful transactions: 40995 Failed transactions: 0 Longest transaction: 0.04 Shortest transaction: 0.00
Not bad at all, considering all we did really was to cache the autoload and configuration data automatically.
The inclued graph looks like this on the first load:
And like this when the cache files have been generated:
As you can probably guess, we won’t be able to improve much more on this part of Web:Extend.
I computed a table with results from a few other frameworks. I used the same method as Rasmus used in his presentation. There’s probably a few frameworks missing, because they’re too complex, because they require me to set up a database, or because I missed them. If you would like one added to this table, feel free to give me its name and instructions on how to write an hello world page with it.
For those who really think an “Hello world!” proves nothing, I also added an example of Web:Extend with a few queries for comparison. The queries include an INSERT statement recording the hits in a table, along with an UPDATE statement and a SELECT statement both working on a 100K rows table. The UPDATE sets one column of one row to NOW() while the SELECT randomly selects 25 rows and display them in the template. This is not a prepared statement so it can be faster.
| Framework | without APC | with APC, apc.stat=1 | with APC, apc.stat=0 | with APC, apc.stat=0, % efficiency |
|---|---|---|---|---|
| PHP | 3965.53 | 4019.18 | 4018.94 | 100% |
| Akelos | 20.28 | 52.40 | 52.47 | 1.3% |
| CakePHP | 49.26 | 183.26 | 181.70 | 4.5% |
| CodeIgniter | 224.36 | 903.99 | 940.07 | 23.4% |
| Kohana | 198.60 | 596.21 | 609.00 | 15.2% |
| Web:Extend | 454.13 | 1359.25 | 1344.81 | 33.5% |
| Web:Extend w/ queries | 223.97 | 426.77 | 418.61 | N/A |
| Zend Framework | 53.03 | 182.11 | 234.53 | 5.8% |
Tests were done with Ubuntu’s default PHP configuration file, on Ubuntu.
A few notes about ZF: since it’s the only framework requiring the setting of include_path, it was tested separately with its library path configured specifically for these tests. The include_path started with the path to the library. Other tests didn’t have any modification of the configuration.
I was kinda surprised to see Kohana being so slow compared to CodeIgniter. As you might not know Kohana is based off CodeIgniter. Maybe there’s a reason? If you know something that could make Kohana slower than CodeIgniter, tell me and I can run the test one more time.
As you can see, there’s a huge difference in performance today between Web:Extend and the other frameworks. Performance-wise, if you’re running PHP5, Web:Extend is your best bet. If you’re still running PHP4, CodeIgniter will get the job done.
There’s still a few things we want to improve in our framework in the next few weeks.
There’s currently 3 application modules: output, database and sessions. We already do a lazy start of the output module, but not of the 2 others. This means that the application will always connect to the database, even when no query are performed. Or that the application will always start the session, even when you don’t need it. Unless of course you didn’t activate these modules in the configuration file at all.
With lazy-loading of the modules, we’ll be able to connect to the database only when weeApp()->db is called. Or start the session only when weeApp()->session is called. The modules won’t be initialized at all if they’re not required for the current page.
Currently the framework generates the templates after the request handling ended. Sometimes you might have some heavy operations to do, like logging, saving a search result, doing some maintenance on the database that the user doesn’t care about. We’ll make it possible to send the output to the browser as soon as possible and perform these other tasks without making the user wait.
Jayson Minard wrote about this recently at devzone: Do Not Make Users Wait for Things They Do Not Care About.
It might also be interesting to send parts of the page early, like the header of the HTML page.
It might be possible to send cached pages directly from the index.php file, which would be really fast but would not be a good choice for all types of applications. We’re still not decided on this. The pros are that the file would be sent very fast, without loading the framework: it would be almost as fast as fetching an HTML file directly. The cons are that it won’t be useable for anything other than simple pages, common to every users. This would be a good choice for vitrine websites or any page that doesn’t require user’s input. We probably will implement it, since our current cache is already “restricted” to common pages. And for other pages the best solution is to use APC or memcache anyway.
Allow the developer to cache the results of a query without having to worry about the caching mechanism used (be it APC, memcache or anything else). The framework would choose the best available on the system and provide a standard interface directly in the model classes. For example, instead of $this->query you could use $this->queryCache to cache the query automatically, with an optional timeout value passed as parameter. You could also use $this->deleteCache to delete the cache corresponding to the parameters given. It could be all the cache for the current model, or only part of it. We would of course provide an option to allow you to choose the caching mechanism used in case the framework doesn’t choose the one you want.
We saw a few days ago how we could analyze all our SELECT queries in one script. This is a functionality offered by our framework today. But this is not enough. We’re still missing a data generator that can handle relational data generation (if you know a good one we could use, please leave a comment!); a builtin profiler to test queries and functions; and a tool to create reports based on the data generated by query analyze and overall benchmark. As you already saw, the analyze of the SELECT queries returns a PHP array. This wasn’t because of laziness, this was because we intend to give you a nice report of these analysis, complete with emphasis on the problematic values (at least for MySQL and PgSQL, anyway).
To quote wikipedia, Simplicity is the property, condition, or quality of being simple or un-combined. It often denotes beauty, purity or clarity. Simple things are usually easier to explain and understand than complicated ones. Simplicity can mean freedom from hardship, effort or confusion.
Code simplicity can be achieved by following a few rules:
And if you ever need guidance in the path of simplicity, ask this good man.
Thanks for reading.
Disclaimer: If you feel like exploring the framework then have fun, but remember that the documentation is still lacking and that some parts of the API may change before we reach beta. We advise you to wait for the beta release before trying to use it, unless you feel adventurous or just plain curious.
November 6th, 2008 by Loïc Hoguin · Tags: Optimization, Simplicity, Web:Extend · 3 Comments
Sorry for not posting until now. I promise we’ll update the blog more often from now on.
Today I’d like to start a series on website optimization. The order in which I will do things is not necessarily the best order to optimize a website. However it will be full of tips and explanations for every single aspect of website optimization. A better structured article will later be based off this series and put in Web:Extend’s documentation. We know, the documentation is pretty empty right now. We intend to fix this before we reach beta.
I currently have a contract with a client whose website had huge performance issues. Our job was first to port his code to our framework, then optimize both the code and the server configuration. When he contacted me he had peaks of 130 registered users at the same time with loads between 15 and 30. Some pages could take up to 30 seconds to load.
So I did just that, port the code to use Web:Extend instead of various PEAR and non-pear libraries. At the same time we would remove the biggest problems in the database schema and in the queries. Let me get this straight: the website was slow not because it used PEAR; it was because the queries would sometimes select thousands of rows that would then be filtered by PHP, and because some operations generated a huge number of queries because JOINs were not used as often as it should have been. The use of the framework was motivated by the client’s will to move to PHP5 instead of the now deprecated PHP4, and this framework was chosen because I’m its main developer and I know it very well.
Once the code was fully ported and tested (fixing at the same time a few bugs in the original code), we put it on the production server. The load reduced to an average of 3 at peak times. That’s nice, but it can be better. I should point here that any user on the website produce an AJAX query (producing several SQL queries) every 10 seconds. That means at least 13 queries per second at peak times. It’s actually more, since that number does not include non-registered users.
The queries were not optimized yet.
We looked into it and decided to first try to find the biggest performance problem. The chat functionality was the biggest problem. Whenever someone posted a new chat message, a noticeable delay would happen. All chat messages since the launch of the website were stored in one table… containing about 1.5 million rows. The table was big and not properly indexed nor optimized (it’s still not perfectly optimized but I’ll get back on this point later). Most chat messages in this table were never used since they were read and long forgotten. We decided to archive the table periodically (deleting wasn’t an option). A copy of the table was thus created and a cron job would run the PHP code to archive all the read messages sent more than 15 days ago and all unread messages sent more than 3 months ago. This gave us a nice 150 thousand rows on the main table instead of the 1.5 million we started with.
This last point helped a lot; the slowness went away. But this didn’t fix everything. More queries needed help, and the database schema was still in need of huge changes.
This part is very technical. Do not hesitate to post a comment asking for answers or clarification. Thank you.
This brings us to the most interesting part of this post. I needed to analyze the SELECT queries used by the application. All the queries. My first thought was: is there a way to analyze everything in a completely automated way without having to copy/paste the queries or rewrite anything? If such a beast exist, I didn’t find it. So I wrote it myself.
This website uses MySQL. A good way to check whether your queries are optimized is to run an EXPLAIN statement. An EXPLAIN statement is basically just a SELECT query with “EXPLAIN ” put at the start. Instead of running the query, it will analyze it and return various informations about how MySQL will run it. This basically means that I just have to put “EXPLAIN ” at the start of all my SQL queries to get a full report on all the queries. And this is basically what I did, but without changing the application’s code.
In Web:Extend, we used the concept of sets to define the model part of applications. A set contains elements, just like a table contains rows, or like a path contains files, directories or links. You can retrieve either “exactly 1″ or “n (with n>=0)” elements from a set. Let’s take an example: the users set. This set have one user with nickname "essen", and 42 users with the "invisible" option activated. In our framework, "essen" is an individual element while the "invisible" users are a subset of the users set. Mathematically, "essen" is both an individual element and a subset, but for the sake of simplicity we consider it to be nothing more than an individual element.
Web:Extend defines base classes for both a set and an individual element. All operations performed on a set (e.g. all SELECT statements, along with the UPDATE and DELETE operations potentially affecting n rows) are written in the set class; all operations performed on an individual element (e.g. INSERT, UPDATE, DELETE affecting only 1 row) are written in the element class. When a set returns elements, it creates the correct instance for the element associated with the given set.
You probably understood by now that all SELECT queries are stored in only one type of class, the set classes, and that these classes do not contain any overhead related to the handling of individual elements. In other words, we’re free to instanciate them at will and call its methods without any worry. Most methods are of the type count, fetch or fetchAll, which respectively count the number of elements in the set (or in a subset), retrieve exactly 1 element and retrieve n elements.
All we have to do now is put an “EXPLAIN ” at the start of all these queries. Now that we saw how it works, it should be quite easy, right? As long as we resolve the following problems:
Let’s take this code for example:
class demoUsersSet extends weeDbSet
{
protected $sModel = 'demoUsersModel';
/**
@return int The number of users.
*/
public function count()
{
$a = $this->getDb()->query('
SELECT COUNT(*) AS c FROM users
')->fetch();
return $a['c'];
}
/**
@param $iUserId The user identifier.
@return demoUsersModel The requested user.
*/
public function fetch($iUserId)
{
return $this->getDb()->query('
SELECT * FROM users WHERE user_id=? LIMIT 1
', $iUserId)->rowClass($this->sModel)->fetch();
}
/**
@return weeDatabaseResults All the users.
*/
public function fetchAll()
{
return $this->getDb()->query('
SELECT * FROM users
')->rowClass($this->sModel);
}
}
We have the array access in count and fetch methods to bypass. We are going to bypass them by writing weeDatabaseResult child classes that will always return $this for both array access and fetch methods. Let’s call the resulting class weeExplainSQLResult (no pun intended).
This won’t be enough, however; we still need to tell the database driver used by the model to return “EXPLAIN” result objects instead of normal results. We can do this simply by extending the database driver classes, make the required changes, and tell the model classes to use that driver for their queries. Extending was easy for both MySQL and PgSQL.
We finished writing the classes that will bypass the execution of all the queries in the model and execute EXPLAIN queries instead.
Let’s try it!
$oDb = new weeExplainMySQLDatabase(array( 'host' => 'localhost', 'user' => 'wee', 'password' => 'wee', 'dbname' => 'wee_tests', )); $oSet = new demoUsersSet; $oSet->setDb($oDb); foreach ($oSet->count() as $aExplainRow) var_dump($aExplainRow); foreach ($oSet->fetch(42) as $aExplainRow) var_dump($aExplainRow); foreach ($oSet->fetchAll() as $aExplainRow) var_dump($aExplainRow);
When ran, this code will output this:
array(10) {
["id"]=>
string(1) "1"
["select_type"]=>
string(6) "SIMPLE"
["table"]=>
NULL
["type"]=>
NULL
["possible_keys"]=>
NULL
["key"]=>
NULL
["key_len"]=>
NULL
["ref"]=>
NULL
["rows"]=>
NULL
["Extra"]=>
string(28) "Select tables optimized away"
}
array(10) {
["id"]=>
string(1) "1"
["select_type"]=>
string(6) "SIMPLE"
["table"]=>
string(5) "users"
["type"]=>
string(5) "const"
["possible_keys"]=>
string(7) "PRIMARY"
["key"]=>
string(7) "PRIMARY"
["key_len"]=>
string(1) "4"
["ref"]=>
string(5) "const"
["rows"]=>
string(1) "1"
["Extra"]=>
string(0) ""
}
array(10) {
["id"]=>
string(1) "1"
["select_type"]=>
string(6) "SIMPLE"
["table"]=>
string(5) "users"
["type"]=>
string(3) "ALL"
["possible_keys"]=>
NULL
["key"]=>
NULL
["key_len"]=>
NULL
["ref"]=>
NULL
["rows"]=>
string(1) "3"
["Extra"]=>
string(0) ""
}
By applying this on all the queries of our application we will get the complete analyze of all our SELECT queries, available for review and optimization to anyone working on the project. These “EXPLAIN” classes will allow us to test the results of changes on the database over all the queries rather than looking at them one by one and possibly missing things by making changes that affect a query we already looked at and fixed.
Feel free to use, improve or adapt this method in your projects. If you do, please let us know about it so we can use your improvements.
We’ll take a break from the query optimization in the next post of this series to talk about Simple is Hard and its implications for our framework, our thoughts about it and what are our plans to improve performance as a whole in the framework. I’ll only say for now that our framework performs really, really well. 3rd part will talk about query optimization and the importance of database design, highlighting errors and explaining good practices.
Thanks for reading, see you soon.
Disclaimer: If you feel like exploring the framework then have fun, but remember that the documentation is still lacking and that some parts of the API may change before we reach beta. We advise you to wait for the beta release before trying to use it, unless you feel adventurous or just plain curious.
October 11th, 2008 by Loïc Hoguin · Tags: Database, Optimization, Web:Extend · 2 Comments
Recently we stumbled upon a bug in our framework. The problem was tricky. We were submitting values to be inserted in an SQL query and some of them weren’t found by the code that actually build the query with all the escaping needed automatically. Why’s that? Because we were passing an ArrayAccess object containing the values instead of a normal array.
It is written deep in the PHP documentation that objects that implement the interface ArrayAccess do not work with the array_* functions. None of them. Not even array_key_exists. You would usually use this function to check if a key exists in the array, even if the associated value is null. But you can’t. Some people wrote bug reports to the PHP team about it. They were marked as bogus.
Of course, no error or warning is triggered when you use it with this function. It would be too easy. And this can leads to mistake, like the bug we found in our code.
So what can we use if array_key_exists doesn’t work?
Instead of array_key_exists we can use the isset function. isset, used on an ArrayAccess object, will call its method offsetExists. This method returns a boolean: whether the offset exists or not.
If we implement offsetExists this way, we can use isset to check if an array has a key, including the ones associated with null values:
// $this->aRow is a real array stored as a protected property of the object
public function offsetExists($offset)
{
return array_key_exists($offset, $this->aRow);
}
Using isset on an ArrayAccess object that implements this function will give the same result as using array_key_exists directly on the object. Example:
$o = new myArrayAccessClass;
$a = array();
// isset, used on an ArrayAccess object...
echo (int)isset($o['key']);
// ... is the same as array_key_exists used on a real array
echo (int)array_key_exists('key', $a);
We also added an unit test about this in our framework. Never knows, maybe array_key_exists is going to accept ArrayAccess objects in the future.
Well, I’m sure you can begin to see the ugly in this: isset giving the same results as array_key_exists on ArrayAccess objects, but different results on real arrays. That means needing to pay more attention to what you actually use these functions on.
Another ugly result is that you will need to do 2 tests when you accept both arrays and objects. If you received an array you’ll have to use array_key_exists, otherwise isset. This results in the following conditional statement:
if ((is_array($m) && array_key_exists($sKey, $m)) || (!is_array($m) && isset($m[$sKey]))) ; // insert code here...
Take care about these issues next time you use ArrayAccess!
Thanks for reading.
March 1st, 2008 by Loïc Hoguin · Tags: PHP5, Tips, Web:Extend · 1 Comment
PHP5 introduced the SimpleXML extension. It allows for easy manipulation of XML documents. This nice extension received a warm welcome in the community. But it unfortunately has its drawbacks.
SimpleXML uses some little hacks to do its tricks. It overloads the -> and [] operators. What that means is that if you extend SimpleXML and create a property in the child class, you will never be able to access this property, because SimpleXML will return a child node and ignore your property. You cannot either use the ArrayAccess interface to store your properties since SimpleXML already overload the [] operator to allow you to access the node’s attributes.
Why would you extend SimpleXML if you can’t set any property? Well you can of course add a few methods, to add missing functionality for example. But that’s about it. No properties. If you need to associate a SimpleXML node with a variable you must either create a child node, or create an attribute. Problem is, it won’t work with objects, arrays or resources.
Someone already tried to do it. Sorry Oliver Brown, but I don’t like your solution. I shall thank you however, since you allowed me to find a better way to do it. I’m sorry about not telling the world sooner about all this.
The solution is currently available in the LGPL licensed Web:Extend framework, in the form of the weeSimpleXMLHack class. Although LGPL licensed, I (the original author) allow you to copy and paste this specific class in your project whatever the license is. In return, please just refer to this post in your code.
For your convenience I will paste the code below.
/**
Extends SimpleXMLIterator to allow the setting and getting of properties.
Properties must be accessed using the function property since -> is already used by SimpleXMLIterator
(and its parent class SimpleXMLElement).
*/
class weeSimpleXMLHack extends SimpleXMLIterator
{
/**
Return the uniqid string for this object.
@return The uniqid string.
*/
protected function hack()
{
if (empty($this['weeuniqidhack']))
$this['weeuniqidhack'] = uniqid();
return (string)$this['weeuniqidhack'];
}
/**
Set or get a property of a SimpleXMLIterator object.
@param $sName The name of the property.
@param $mParam The variable to attach to this element. If null it is not attached.
@return mixed The variable attached with the specified name.
*/
public function property($sName, $mParam = null)
{
static $aProperties = null;
if (!is_null($mParam))
$aProperties[$this->hack()][$sName] = $mParam;
if (!array_key_exists($sName, $aProperties[$this->hack()]))
throw new IllegalStateException("The property doesn't exist.");
return $aProperties[$this->hack()][$sName];
}
}
If you compare this code and the original code from the framework you will notice a few differences. I’ve edited it a bit to make it more readable for people that do not know how the fire function works.
So how does it work? Well as you know you can create a method. In this method you can create a static variable that will be accessible only by the code in the method. Make this variable an array of properties. By making the method usable both as a getter and a setter you can set and get properties from this static array.
But that’s not enough. As you might already know, a static variable declared in a method will keep the same value for all the objects of the class. That is, if object #1 sets a static variable value to 1, then object #2 sets it to 2, it will have a value of 2 when object #1 tries to access it again. We do not want that since we want to be able to associate objects and more to our SimpleXML objects.
That’s where uniqid comes. uniqid creates a completely unique identifier. Great! We can now assign an unique identifier to each of our SimpleXML objects, and use that identifier to retrieve the properties of that object. We only call uniqid once and store it as a reserved attribute named weeuniqidhack. When the property method is called we can just get that identifier and do a get or a set on our static array without fearing conflicts with other SimpleXML objects properties.
You can now use this class by calling simplexml_load_string or simplexml_load_file and giving the class name, 'weeSimpleXMLHack', as the second parameter:
$oNode = simplexml_load_file('/path/to/my/file.xml', 'weeSimpleXMLHack');
This hack was used for more than a year in the Web:Extend framework and worked since the early releases of PHP5, so you can use it without any fear. The only drawback is that an attribute must be sacrificed to store the unique identifier.
I have other tips to share but they will be for another day. You might find some by browsing the framework’s code.
Thanks for reading.
February 20th, 2008 by Loïc Hoguin · Tags: PHP5, Tips, Web:Extend · 9 Comments
Hello and welcome to the all-new Web:Extend blog. Web:Extend is a PHP5 web framework designed to let you think instead of writing code. Our goals is to make you write less, better code. Web:Extend can be used either as a framework or as a library. It is cut into various modules like form processing, database abstraction layer, output control with templates, data validation, application layer, etc.
Our main focus currently is to create a core with all the essential components ready to use. Sorry folks, no web-services yet. You can however submit a module for inclusion if you feel like helping; we won’t stop you.
Enough said, please head over to the project page to learn more about Web:Extend, including some examples, and a list of features. Our philosophy is also briefly described in the documentation, you might find it worth a read.
Thanks for reading, and see you soon for more posts on the magic behind this framework.
February 18th, 2008 by Loïc Hoguin · Tags: Web:Extend · 1 Comment