<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" version="2.0">

<channel>
	<title>Jasny · web development</title>
	
	<link>http://www.jasny.net</link>
	<description>Helping you out with PHP &amp; MySQL</description>
	<lastBuildDate>Tue, 08 Nov 2011 15:52:09 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/Jasny" /><feedburner:info xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="jasny" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item>
		<title>A secure backdoor for PHP</title>
		<link>http://www.jasny.net/articles/a-secure-backdoor-for-php/</link>
		<comments>http://www.jasny.net/articles/a-secure-backdoor-for-php/#comments</comments>
		<pubDate>Tue, 11 May 2010 14:48:23 +0000</pubDate>
		<dc:creator>Arnold Daniels</dc:creator>
				<category><![CDATA[Authentication]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://www.jasny.net/?p=415</guid>
		<description><![CDATA[A backdoor provides access to an application through a different method that the normal authentication process. There are many ways to do this. Some are more secure than others.]]></description>
			<content:encoded><![CDATA[<!-- Start Shareaholic LikeButtonSetTop Automatic --><div style="clear: both; min-height: 1px; height: 3px; width: 100%;"></div><div class='shareaholic-like-buttonset' style='float:none;height:30px;'><a class='shareaholic-fblike' data-shr_layout='button_count' data-shr_showfaces='false' data-shr_href='http%3A%2F%2Fwww.jasny.net%2Farticles%2Fa-secure-backdoor-for-php%2F' data-shr_title='A+secure+backdoor+for+PHP'></a><a class='shareaholic-fbsend' data-shr_href='http%3A%2F%2Fwww.jasny.net%2Farticles%2Fa-secure-backdoor-for-php%2F'></a><a class='shareaholic-googleplusone' data-shr_size='medium' data-shr_count='true' data-shr_href='http%3A%2F%2Fwww.jasny.net%2Farticles%2Fa-secure-backdoor-for-php%2F' data-shr_title='A+secure+backdoor+for+PHP'></a></div><div style="clear: both; min-height: 1px; height: 3px; width: 100%;"></div><!-- End Shareaholic LikeButtonSetTop Automatic --><p>A <a href="http://en.wikipedia.org/wiki/Backdoor_%28computing%29" target="_blank">backdoor</a> provides access to an application bypassing the normal authentication process. There are many ways to do this. Some are more secure than others.</p>
<h3>Why do you need a backdoor?</h3>
<p>In a perfect word you could just deliver an application and all would be good. However in the real world there are unforeseen issues which need to be solved. This means that you as a developer will need access to the application. To reproduce the problem, you usually want to run the application logged in as the user that spotted the issue.</p>
<p>Another use of the backdoor is in a situation where you want to allow a user, that has already been authenticated, to bypassing further authentication. For example if you have a (web hosting) control panel where the user is already logged in, you can allow him to directly access the dashboard of the application without have to enter his password again. This requires a backdoor, since you don&#8217;t know his (unencrypted) password.<br />
<span id="more-415"></span></p>
<h3>A very simple solution</h3>
<p>The most simple solution is to use a backdoor password. This password will work for every user. A variation on this, is to have a superuser account, that is allowed to switch to any user on the system.</p>
<p>This solution is fine if you&#8217;re the only developer working on these applications. However in a professional environment this solution won&#8217;t do. With this method is easy to give somebody super privileges, but hard to take them away. This requires changing the backdoor password. Which is a tedious job if you&#8217;re managing any serious number of applications.</p>
<h3>The secure way</h3>
<p>It is easier if there is a project management system where you and other developers can log into. From within that system, the developer can directly login the customer application as any user. Within that application you can configure on which team each developer is. That limits to which applications the developer has access. More important, simply blocking the user account on the project management system will lock the developer out completely.</p>
<h3>Private and public keys</h3>
<p>The best known method for logging into a system, is the use of private/public (DSA) keys with SSH. The SSH client signs the request with the private key. The SSH server has the public key in the authorized_key file. It verifies the credentials using the public keys and grands access on success.</p>
<p>We can use the same method with PHP using the <a href="http://php.net/openssl" title="OpenSSL in PHP manual" target="_blank">OpenSLL</a> extension. We&#8217;ll let the client (project management system) sign the username and system name (URL) using openssl_sign. This signature is verified on the server (customer application) using openssl_verify. To unsure the login URL can&#8217;t be reused later, we&#8217;ll throw in a 5 second timeout.</p>
<h3>Generating the keys</h3>
<p>The keys can be generated on the (*nix) command line, using the &#8216;openssl&#8217; binary. I&#8217;m using RSA keys, but DSA should also work if preferred.</p>
<pre># Generate private key
openssl genrsa -out master.key 1024
# Generate public key
openssl rsa -in master.key -pubout -out master.pub</pre>
<p>The public key should be copied to the &#8216;pubkeys&#8217; directory of the server application. Make sure the private key is absolutely private. Anybody who has a copy of that, can use the backdoor.</p>
<h3>Download</h3>
<p><i>Don&#8217;t use the downloaded code without replacing the private and public key!</i><br />
<a href="http://github.com/jasny/backdoor/archives/master" target="_blank">Download the code @ github</a><br />
<a href="http://www.jasny.net/code/backdoor/client/" target="_blank">A (not to impressive) demo</a></p>
<p>If I overlooked security issues with this implementation, please leave a comment below.</p>
<div class="shr-publisher-415"></div><!-- Start Shareaholic LikeButtonSetBottom Automatic --><!-- End Shareaholic LikeButtonSetBottom Automatic -->]]></content:encoded>
			<wfw:commentRss>http://www.jasny.net/articles/a-secure-backdoor-for-php/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Versioning MySQL data: Multi-table records</title>
		<link>http://www.jasny.net/articles/versioning-mysql-data-multi-table-records/</link>
		<comments>http://www.jasny.net/articles/versioning-mysql-data-multi-table-records/#comments</comments>
		<pubDate>Thu, 26 Nov 2009 01:41:45 +0000</pubDate>
		<dc:creator>Arnold Daniels</dc:creator>
				<category><![CDATA[MySQL, Sphinx and NoSQL]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://www.adaniels.nl/?p=342</guid>
		<description><![CDATA[In the article &#8216;Versioning MySQL data&#8216;, I showed the basics of implementing a revisioning system using trigger. As Jens Schauder already pointed out, often the data of a record is spread across multiple tables, like an invoice with multiple invoice lines. Having each invoice line versioned individually isn&#8217;t really useful. Instead we want a new <a href='http://www.jasny.net/articles/versioning-mysql-data-multi-table-records/'>[...]</a>]]></description>
			<content:encoded><![CDATA[<!-- Start Shareaholic LikeButtonSetTop Automatic --><div style="clear: both; min-height: 1px; height: 3px; width: 100%;"></div><div class='shareaholic-like-buttonset' style='float:none;height:30px;'><a class='shareaholic-fblike' data-shr_layout='button_count' data-shr_showfaces='false' data-shr_href='http%3A%2F%2Fwww.jasny.net%2Farticles%2Fversioning-mysql-data-multi-table-records%2F' data-shr_title='Versioning+MySQL+data%3A+Multi-table+records'></a><a class='shareaholic-fbsend' data-shr_href='http%3A%2F%2Fwww.jasny.net%2Farticles%2Fversioning-mysql-data-multi-table-records%2F'></a><a class='shareaholic-googleplusone' data-shr_size='medium' data-shr_count='true' data-shr_href='http%3A%2F%2Fwww.jasny.net%2Farticles%2Fversioning-mysql-data-multi-table-records%2F' data-shr_title='Versioning+MySQL+data%3A+Multi-table+records'></a></div><div style="clear: both; min-height: 1px; height: 3px; width: 100%;"></div><!-- End Shareaholic LikeButtonSetTop Automatic --><p>In the article &#8216;<a href="http://www.adaniels.nl/articles/versioning-mysql-data/">Versioning MySQL data</a>&#8216;, I showed the basics of implementing a revisioning system using trigger. As Jens Schauder already pointed out, often the data of a record is spread across multiple tables, like an invoice with multiple invoice lines. Having each invoice line versioned individually isn&#8217;t really useful. Instead we want a new revision of the whole invoice on each change.</p>
<p><strong>The perfect solution</strong><br />
Ideally a change of one or more parts of the invoice would be changed, a new revision would be created. There are several issues in actually creating this those. Detecting the change of multiple parts of the invoice at once, generating a single revision, would mean we need to know if the actions are done within the same transaction. Unfortunately there is a connection_id(), but no transaction_id() function in MySQL. Also, the <a href="http://dev.mysql.com/doc/refman/5.1/en/error-messages-server.html#error_er_cant_update_used_table_in_sf_or_trg" target="_blank">query would fail</a> when a query inserts or updates a record in the child table, using the parent table. We need to come up with something else.</p>
<p>In the implementation we currently have in production, we version the rows in the parent as well in the child tables. For each version of the parent row, we register which versions of the child rows ware set. This however has really complicated the trigger code and tends to need a lot of checking an querying slowing the write process down. Since nobody ever looks at the versions of the child rows, the application forces a new version of the parent row. The benefits of versioning both are therefor minimal.</p>
<p><strong>Only versioning the parent</strong><br />
For this new (simplified) implementation, we will only have one revision number across all tables of the record. Changing data from the parent table, will trigger a new version. This will not only copy the parent row to the revisioning table, but also the rows of the children.</p>
<p>Writing to the child will not trigger a new version, instead it will update the data in the revisioning table. This means that when changing the record, you need to write to the parent table, before writing to the child tables. To force a new version without changing values use</p>
<pre>UPDATE mytable SET _revision=NULL where id=$id</pre>
<p><span id="more-342"></span></p>
<p>The parent and child tables are defined as</p>
<pre>CREATE TABLE `mytable` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL DEFAULT '',
  `description` text,
  PRIMARY KEY (`id`),
  UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB

CREATE TABLE `mychild` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `mytable_id` int(10) unsigned NOT NULL DEFAULT '0',
  `title` varchar(255) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`),
  KEY `mytable_id` (`mytable_id`),
  CONSTRAINT `mychild_ibfk_1` FOREIGN KEY (`mytable_id`) REFERENCES `mytable` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB</pre>
<p>Note that we are using InnoDB tables here. MyISAM doesn&#8217;t have foreign key constraints, therefor it&#8217;s not possible to define a parent-child relationship.</p>
<p><strong>Insert, update and delete</strong><br />
In the parent trigger, two different things happen concerning the child rows. When a new version is created, the data of `mychild` is copied to the revisioning table. On a revision switch, data will be copied from the revisioning table into `mychild`. The &#8220;`_revision_action` IS NULL&#8221; condition, means that `_revision_mytable` is only updated when a new revision is created.</p>
<pre>CREATE TRIGGER `mytable-afterupdate` AFTER update ON `mytable`
  FOR EACH ROW BEGIN
    DECLARE `newrev` BOOLEAN;

    UPDATE `_revision_mytable` SET `id` = NEW.`id`, `name` = NEW.`name`, `description` = NEW.`description`, `_revision_action`='update' WHERE `_revision`=NEW.`_revision` AND `_revision_action` IS NULL;
    SET newrev = (ROW_COUNT() &gt; 0);
    INSERT INTO `_revhistory_mytable` VALUES (NEW.`id`, NEW.`_revision`, @auth_uid, NOW());

    IF newrev THEN
       INSERT INTO `_revision_mychild` SELECT *, NEW.`_revision` FROM `mychild` WHERE `mytable_id` = NEW.`id`;
    ELSE
       DELETE `t`.* FROM `mychild` AS `t` LEFT JOIN `_revision_mychild` AS `r` ON 0=1 WHERE `t`.`mytable_id` = NEW.`id`;
       INSERT INTO `mychild` SELECT `id`, `mytable_id`, `title` FROM `_revision_mychild` WHERE `_revision` = NEW.`_revision`;
    END IF;
  END

CREATE TRIGGER `mychild-afterinsert` AFTER INSERT ON `mychild`
  FOR EACH ROW BEGIN
    DECLARE CONTINUE HANDLER FOR 1442 BEGIN END;
    INSERT IGNORE INTO `_revision_mychild` (`id`, `mytable_id`, `title`, `_revision`) SELECT NEW.`id`, NEW.`mytable_id`, NEW.`title`, `_revision` FROM `mytable` AS `p` WHERE `p`.`id`=NEW.`mytable_id`;
  END

CREATE TRIGGER `mychild-afterupdate` AFTER UPDATE ON `mychild`
  FOR EACH ROW BEGIN
    REPLACE INTO `_revision_mychild` (`id`, `mytable_id`, `title`, `_revision`) SELECT NEW.`id`, NEW.`mytable_id`, NEW.`title`, `_revision` FROM `mytable` AS `p` WHERE `p`.`id`=NEW.`mytable_id`;
  END

CREATE TRIGGER `mychild-afterdelete` AFTER DELETE ON `mychild`
  FOR EACH ROW BEGIN
    DECLARE CONTINUE HANDLER FOR 1442 BEGIN END;
    DELETE `r`.* FROM `_revision_mychild` AS `r` INNER JOIN `mytable` AS `p` ON `r`.`_revision` = `p`.`_revision` WHERE `r`.`id` = OLD.`id`;
  END</pre>
<p>Changing data in table `mychild` simply updates the data in the revisioning table. The revision number is grabbed from the field in the parent table.</p>
<p>Switching the revision can only be done through the parent table. This will also automatically change the data in the child tables. We simply delete all rows of the record and replace them with data from the revisioning table. This would however trigger the deletion of the data in `_revision_child` on which the insert has nothing to do. To prevent this, we can abuse that fact that a trigger can&#8217;t update data of a table using in the insert/update/delete query. This causes <a href="http://dev.mysql.com/doc/refman/5.1/en/error-messages-server.html#error_er_cant_update_used_table_in_sf_or_trg">error 1442</a>. With a continue handler we can ignore this silently.</p>
<p>The InnoDB constraints will handle the cascading delete. Deleting child data <a href="http://dev.mysql.com/doc/refman/5.4/en/innodb-foreign-key-constraints.html" target="_blank">won&#8217;t activate the deletion trigger</a>, which is all the better in this case.</p>
<p><strong>Without a primary key</strong><br />
A primary key is not required for the child table, since versioning is done purely based on the id of `mytable`.</p>
<pre>CREATE TABLE `mypart` (
  `mytable_id` int(10) unsigned NOT NULL,
  `reference` varchar(255) NOT NULL,
  KEY `mytable_id` (`mytable_id`),
  CONSTRAINT `mypart_ibfk_1` FOREIGN KEY (`mytable_id`) REFERENCES `mytable` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB</pre>
<p>This does cause an issue for the update and delete triggers of the child table. It can&#8217;t use the primary to id to locate the current version of the modified/removed row. This can be solved by a trick I got from PhpMyAdmin. We can simply locate the record by comparing the old values of all fields. There is no constraint for the table enforcing the uniqueness of a row, so we could be targeting multiple identical rows. Since they are identical, it doesn&#8217;t matter which one we target, as long as we limit to 1 row.</p>
<pre>CREATE TRIGGER `mypart-afterupdate` AFTER UPDATE ON `mypart`
  FOR EACH ROW BEGIN
    DELETE FROM `_revision_mypart` WHERE `_revision` IN (SELECT `_revision` FROM `mytable` WHERE `id` = OLD.`mytable_id`) AND `mytable_id` = OLD.`mytable_id` AND `reference` = OLD.`reference` LIMIT 1;
    INSERT INTO `_revision_mypart` (`mytable_id`, `reference`, `_revision`) SELECT NEW.`mytable_id`, NEW.`reference`, `_revision` FROM `mytable` AS `p` WHERE `p`.`id`=NEW.`mytable_id`;
  END

CREATE TRIGGER `mypart-afterdelete` AFTER DELETE ON `mypart`
  FOR EACH ROW BEGIN
    DECLARE CONTINUE HANDLER FOR 1442 BEGIN END;
    DELETE FROM `_revision_mypart` WHERE `_revision` IN (SELECT `_revision` FROM `mytable` WHERE `id` = OLD.`mytable_id`) AND `mytable_id` = OLD.`mytable_id` AND `reference` = OLD.`reference` LIMIT 1;
  END</pre>
<p><strong>Unique keys</strong><br />
The revisioning table has multiple versions of a record. Unique indexes from the original table should be converted to non-unique indexes in the revisioning table. This information can be fetched using INFORMATION_SCHEMA.</p>
<pre>SELECT c.CONSTRAINT_NAME, GROUP_CONCAT(CONCAT('`', k.COLUMN_NAME, '`')) AS cols FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS `c` INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS `k` ON c.TABLE_SCHEMA=k.TABLE_SCHEMA AND c.TABLE_NAME=k.TABLE_NAME AND c.CONSTRAINT_NAME=k.CONSTRAINT_NAME WHERE c.TABLE_SCHEMA=DATABASE() AND c.TABLE_NAME='mytable' AND c.CONSTRAINT_TYPE='UNIQUE' AND c.CONSTRAINT_NAME != '_revision' GROUP BY c.CONSTRAINT_NAME</pre>
<p><strong>Revisioning and replication</strong><br />
Baron Schwartz pointed out a <a href="http://www.mysqlperformanceblog.com/2008/09/29/why-audit-logging-with-triggers-in-mysql-is-bad-for-replication/" target="_blank">race condition</a> when relying on auto-increment keys in triggers with replication. Actions carried out through triggers on a master are <a href="http://dev.mysql.com/doc/refman/5.0/en/faqs-triggers.html#qandaitem-22-5-1-12" target="_blank">not replicated to a slave server</a>. Instead, <a href="http://dev.mysql.com/doc/refman/5.0/en/faqs-triggers.html#qandaitem-22-5-1-10" target="_blank">triggers on the slave will be invoked</a>, which should do the same action as on the master.</p>
<p>It probably isn&#8217;t needed to have a copy of the revisioning tables on the slave. This would mean that we could simply omit the triggers. Unfortunately this causes problems when changing the revision. In that case we are forced to move switching of a revision out of the database. Instead the application needs to select the data from all revisioning tables and write that to the original tables. Any other thoughts on solving this issue are welcome.</p>
<p><strong id="download">Download</strong><br />
<a href='http://github.com/jasny/mysql-revisioning/archives/master'>&#8211;> Download mysql-revisioning script @github <--</a></p>
<div class="shr-publisher-342"></div><!-- Start Shareaholic LikeButtonSetBottom Automatic --><!-- End Shareaholic LikeButtonSetBottom Automatic -->]]></content:encoded>
			<wfw:commentRss>http://www.jasny.net/articles/versioning-mysql-data-multi-table-records/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Versioning MySQL data</title>
		<link>http://www.jasny.net/articles/versioning-mysql-data/</link>
		<comments>http://www.jasny.net/articles/versioning-mysql-data/#comments</comments>
		<pubDate>Thu, 12 Nov 2009 16:36:18 +0000</pubDate>
		<dc:creator>Arnold Daniels</dc:creator>
				<category><![CDATA[Ajax]]></category>
		<category><![CDATA[MySQL, Sphinx and NoSQL]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://www.adaniels.nl/?p=291</guid>
		<description><![CDATA[Advantages of a VCS above a nightly backup are that you can walk to the individual changes for a document, see who made each change and revert back to specific revision if needed. These are features which would also be nice for data stored in a database. This article shows how to use triggers to can implement versioning for data stored in a MySQL db.]]></description>
			<content:encoded><![CDATA[<!-- Start Shareaholic LikeButtonSetTop Automatic --><div style="clear: both; min-height: 1px; height: 3px; width: 100%;"></div><div class='shareaholic-like-buttonset' style='float:none;height:30px;'><a class='shareaholic-fblike' data-shr_layout='button_count' data-shr_showfaces='false' data-shr_href='http%3A%2F%2Fwww.jasny.net%2Farticles%2Fversioning-mysql-data%2F' data-shr_title='Versioning+MySQL+data'></a><a class='shareaholic-fbsend' data-shr_href='http%3A%2F%2Fwww.jasny.net%2Farticles%2Fversioning-mysql-data%2F'></a><a class='shareaholic-googleplusone' data-shr_size='medium' data-shr_count='true' data-shr_href='http%3A%2F%2Fwww.jasny.net%2Farticles%2Fversioning-mysql-data%2F' data-shr_title='Versioning+MySQL+data'></a></div><div style="clear: both; min-height: 1px; height: 3px; width: 100%;"></div><!-- End Shareaholic LikeButtonSetTop Automatic --><p>As a developer you&#8217;re probably using a versioning control system, like subversion or git, to safeguard your data. Advantages of using a VCS are that you can walk to the individual changes for a document, see who made each change and revert back to specific revision if needed. These are features which would also be nice for data stored in a database. With the use of <a href="http://dev.mysql.com/doc/refman/5.0/en/triggers.html" target="_blank">triggers</a> we can implement versioning for data stored in a MySQL db.</p>
<p><strong>The revisioning table</strong><br />
We will not store the different versions of the records in the original table. We want this solution to be in the database layer instead of putting all the logic in the application layer. Instead we&#8217;ll create a new table, which stores all the different versions and lives next to the original table, which only contains the current version of each record. This revisioning table is copy of the original table, with a couple of additional fields.</p>
<pre>CREATE TABLE `_revision_mytable` LIKE `mytable`;

ALTER TABLE `_revision_mytable`
  CHANGE `id` `id` int(10) unsigned,
  DROP PRIMARY KEY,
  ADD `_revision` bigint unsigned AUTO_INCREMENT,
  ADD `_revision_previous` bigint unsigned NULL,
  ADD `_revision_action` enum('INSERT','UPDATE') default NULL,
  ADD `_revision_user_id` int(10) unsigned NULL,
  ADD `_revision_timestamp` datetime NULL default NULL,
  ADD `_revision_comment` text NULL,
  ADD PRIMARY KEY (`_revision`),
  ADD INDEX (`_revision_previous`),
  ADD INDEX `org_primary` (`id`);</pre>
<p>The most important field is `_revision`. This field contains a unique identifier for a version of a record from the table. Since this is the unique identifier in the revisioning table, the original id field becomes a normal (indexed) field.<br />
<span id="more-291"></span></p>
<p>We&#8217;ll also store some additional information in the revisioning table. The `_revision_previous` field hold the revision nr of the version that was updated to create this revision. Field `_revision_action` holds the action that was executed to create this revision. This field has an extra function that will discussed later. The user id and timestamp are useful for blaming changes on someone. We can add some comment per revision.</p>
<p>The database user is probably always the same. Storing this in the user id field is not useful. Instead, we can set variable @auth_id after logging in and on connecting to the database to the session user.</p>
<p><strong>Altering the original table</strong><br />
The original table needs 2 additional fields: `_revision` and `_revision_comment`. The `_revision` field holds the current active version. The field can also be used to revert to a different revision. The value of `_revision_comment` set on an update or insert will end up in the revisioning table. The field in the original table will always be empty.</p>
<pre>ALTER TABLE `mytable`
  ADD `_revision` bigint unsigned NULL,
  ADD `_revision_comment` text NULL,
  ADD UNIQUE INDEX (`_revision`);</pre>
<p><strong>The history table</strong><br />
Saving each version is not enough. Since we can revert back to older revisions and of course delete the record altogether, we want to store which version of the record was enabled at what time. The history table only needs to hold the revision number and a timestamp. We&#8217;ll add the primary key fields, so it&#8217;s easier to query. A user id field is included again to blame.</p>
<pre>CREATE TABLE `_revhistory_mytable` (
  `id` int(10) unsigned,
  `_revision` bigint unsigned NULL,
  `_revhistory_user_id` int(10) unsigned NULL,
  `_revhistory_timestamp` timestamp NOT NULL default CURRENT_TIMESTAMP,
  INDEX (`id`),
  INDEX (_revision),
  INDEX (_revhistory_user_id),
  INDEX (_revhistory_timestamp)
) ENGINE=InnoDB;</pre>
<p><strong>How to use</strong><br />
Inserting, updating and deleting data should work as normal, including the <a href="http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html" target="_blank">INSERT &#8230; ON DUPLICATE KEY UPDATE syntax</a>. When updating the _revision field shouldn&#8217;t be changed.</p>
<p>To switch to a different version, we would do something like</p>
<pre>UPDATE mytable SET _revision=$rev WHERE id=$id;</pre>
<p>However if the record has been deleted, there will be no record in the original table, therefore the update won&#8217;t do anything. Instead we could insert a record, specifying the revision.</p>
<pre>INSERT INTO mytable SET _revision=$rev;</pre>
<p>We can combine these two into a statement that works either way.</p>
<pre>INSERT INTO mytable SET id=$id, _revision=$rev ON DUPLICATE KEY UPDATE _revision=VALUES(_revision);</pre>
<p>The above query shows that there an additional constraint. The only thing that indicates that different versions is of the same record, is the primary key. Therefore value of the primary key can&#8217;t change on update. This might mean that some tables need to start using surrogate keys if they are not.</p>
<p><strong>On Insert</strong><br />
Let&#8217;s dive into the triggers. We&#8217;ll start with before insert. This trigger should get the values of a revision when the _revision field is set, or otherwise add a new row to the revision table.</p>
<pre>CREATE TRIGGER `mytable-beforeinsert` BEFORE INSERT ON `mytable`
  FOR EACH ROW BEGIN
    DECLARE `var-id` int(10) unsigned;
    DECLARE `var-title` varchar(45);
    DECLARE `var-body` text;
    DECLARE `var-_revision` BIGINT UNSIGNED;
    DECLARE revisionCursor CURSOR FOR SELECT `id`, `title`, `body` FROM `_revision_mytable` WHERE `_revision`=`var-_revision` LIMIT 1;

    IF NEW.`_revision` IS NULL THEN
      INSERT INTO `_revision_mytable` (`_revision_comment`, `_revision_user_id`, `_revision_timestamp`) VALUES (NEW.`_revision_comment`, @auth_uid, NOW());
      SET NEW.`_revision` = LAST_INSERT_ID();
    ELSE
      SET `var-_revision`=NEW.`_revision`;
      OPEN revisionCursor;
      FETCH revisionCursor INTO `var-id`, `var-title`, `var-body`;
      CLOSE revisionCursor;

      SET NEW.`id` = `var-id`, NEW.`title` = `var-title`, NEW.`body` = `var-body`;
    END IF;

    SET NEW.`_revision_comment` = NULL;
  END

CREATE TRIGGER `mytable-afterinsert` AFTER INSERT ON `mytable`
  FOR EACH ROW BEGIN
    UPDATE `_revision_mytable` SET `id` = NEW.`id`, `title` = NEW.`title`, `body` = NEW.`body`, `_revision_action`='INSERT' WHERE `_revision`=NEW.`_revision` AND `_revision_action` IS NULL;
    INSERT INTO `_revhistory_mytable` VALUES (NEW.`id`, NEW.`_revision`, @auth_uid, NOW());
  END</pre>
<p>If the `_revision` field is NULL, we insert a new row into the revision table. This action is primarily to get a revision number. We set the comment, user id and timestamp. We won&#8217;t set the values, action and previous id yet. The insert might fail or be converted into an update action by insert on duplicate key update. If the insert action fails, we&#8217;ll have an unused row in the revisioning table. This is a problem, since the primary key has not been set, so it won&#8217;t show up anywhere. We can clean up these phantom records once in a while to keep the table clean.</p>
<p>When `_revision` is set, we use a cursor to get the values from the revision table. We can&#8217;t fetch to values directly into NEW, therefore we first fetch them into variables and than copy that into NEW.</p>
<p>After insert, we&#8217;ll update the revision, setting the values and the action. However, the insert might have been an undelete action. In that case `_revision_action` is already set and we don&#8217;t need to update the revision. We also add an entry in the history table.</p>
<p><strong>On Update</strong><br />
The before and after update trigger do more or less the same as the before and after insert trigger.</p>
<pre>CREATE TRIGGER `mytable-beforeupdate` BEFORE UPDATE ON `mytable`
  FOR EACH ROW BEGIN
    DECLARE `var-id` int(10) unsigned;
    DECLARE `var-title` varchar(45);
    DECLARE `var-body` text;
    DECLARE `var-_revision` BIGINT UNSIGNED;
    DECLARE `var-_revision_action` enum('INSERT','UPDATE','DELETE');
    DECLARE revisionCursor CURSOR FOR SELECT `id`, `title`, `body`, `_revision_action` FROM `_revision_mytable` WHERE `_revision`=`var-_revision` LIMIT 1;

    IF NEW.`_revision` = OLD.`_revision` THEN
      SET NEW.`_revision` = NULL;

    ELSEIF NEW.`_revision` IS NOT NULL THEN
      SET `var-_revision` = NEW.`_revision`;

      OPEN revisionCursor;
      FETCH revisionCursor INTO `var-id`, `var-title`, `var-body`, `var-_revision_action`;
      CLOSE revisionCursor;

      IF `var-_revision_action` IS NOT NULL THEN
        SET NEW.`id` = `var-id`, NEW.`title` = `var-title`, NEW.`body` = `var-body`;
      END IF;
    END IF;

    IF (NEW.`id` != OLD.`id` OR NEW.`id` IS NULL != OLD.`id` IS NULL) THEN
-- Workaround for missing SIGNAL command
      DO `Can't change the value of the primary key of table 'mytable' because of revisioning`;
    END IF;

    IF NEW.`_revision` IS NULL THEN
      INSERT INTO `_revision_mytable` (`_revision_previous`, `_revision_comment`, `_revision_user_id`, `_revision_timestamp`) VALUES (OLD.`_revision`, NEW.`_revision_comment`, @auth_uid, NOW());
      SET NEW.`_revision` = LAST_INSERT_ID();
    END IF;

    SET NEW.`_revision_comment` = NULL;
  END

CREATE TRIGGER `mytable-afterupdate` AFTER UPDATE ON `mytable`
  FOR EACH ROW BEGIN
    UPDATE `_revision_mytable` SET `id` = NEW.`id`, `title` = NEW.`title`, `body` = NEW.`body`, `_revision_action`='UPDATE' WHERE `_revision`=NEW.`_revision` AND `_revision_action` IS NULL;
    INSERT INTO `_revhistory_mytable` VALUES (NEW.`id`, NEW.`_revision`, @auth_uid, NOW());
  END</pre>
<p>If `_revision` is not set, it has the old value. In that case a new revision should be created. Setting `_revision` to NULL will have the same behaviour of not setting `_revision`. Next to the comment, user id and timestamp, we add also set the previous revision.</p>
<p>As said before, it&#8217;s very important that the value of primary key doesn&#8217;t change. We need to check this and trigger an error, if it would be changed.</p>
<p><strong>On Delete</strong><br />
Deleting won&#8217;t create a new revisiong. However we do want to log that the record has been deleted. Therefore we add an entry to the history table with `_revision` set to NULL.</p>
<pre>CREATE TRIGGER `mytable-afterdelete` AFTER DELETE ON `mytable`
  FOR EACH ROW BEGIN
    INSERT INTO `_revhistory_mytable` VALUES (OLD.`id`, NULL, @auth_uid, NOW());
  END</pre>
<p><strong>To conclude</strong><br />
Using triggers we can implement the basic versioning functionality to MySQL. Since this is completely done the by database, it can be added to an existing application, without having to change to application code (or with very little changes). Using the history table, we can get the data of the database on any moment in time.</p>
<p>There are some situations where this solution as a bit to basic. A record might span across multiple table, like an invoice with invoice lines. In that case, we don&#8217;t want to revision each individual invoice line, but the invoice as a whole. I&#8217;ll come around in a follow up with a solution for this. I can tell up front that this solution is unfortunately not as clean as these basics.</p>
<p><strong>Continue reading</strong><br />
Please continue reading the follow up article &#8216;<a href="http://www.adaniels.nl/articles/versioning-mysql-data-multi-table-records/">Versioning MySQL data: Multi-table records</a>&#8216;. At the bottom of that article you&#8217;ll find <a href="http://www.adaniels.nl/articles/versioning-mysql-data-multi-table-records/#download">a download link</a> for a script that adds revisioning to existing MySQL tables.</p>
<div class="shr-publisher-291"></div><!-- Start Shareaholic LikeButtonSetBottom Automatic --><!-- End Shareaholic LikeButtonSetBottom Automatic -->]]></content:encoded>
			<wfw:commentRss>http://www.jasny.net/articles/versioning-mysql-data/feed/</wfw:commentRss>
		<slash:comments>15</slash:comments>
		</item>
		<item>
		<title>EAV multi-value fields</title>
		<link>http://www.jasny.net/articles/eav-multi-value-fields/</link>
		<comments>http://www.jasny.net/articles/eav-multi-value-fields/#comments</comments>
		<pubDate>Wed, 28 Oct 2009 16:59:51 +0000</pubDate>
		<dc:creator>Arnold Daniels</dc:creator>
				<category><![CDATA[MySQL, Sphinx and NoSQL]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://www.adaniels.nl/?p=278</guid>
		<description><![CDATA[In the article &#8216;An alternative way of EAV modelling&#8217;, I discussed how to do EAV modelling by casting all values (except text) to integers. I&#8217;ll continue on that and talk about more advanced topics like multi-value fields. As binary set Not all questions have only a single option. Some fields we want to represent by <a href='http://www.jasny.net/articles/eav-multi-value-fields/'>[...]</a>]]></description>
			<content:encoded><![CDATA[<!-- Start Shareaholic LikeButtonSetTop Automatic --><div style="clear: both; min-height: 1px; height: 3px; width: 100%;"></div><div class='shareaholic-like-buttonset' style='float:none;height:30px;'><a class='shareaholic-fblike' data-shr_layout='button_count' data-shr_showfaces='false' data-shr_href='http%3A%2F%2Fwww.jasny.net%2Farticles%2Feav-multi-value-fields%2F' data-shr_title='EAV+multi-value+fields'></a><a class='shareaholic-fbsend' data-shr_href='http%3A%2F%2Fwww.jasny.net%2Farticles%2Feav-multi-value-fields%2F'></a><a class='shareaholic-googleplusone' data-shr_size='medium' data-shr_count='true' data-shr_href='http%3A%2F%2Fwww.jasny.net%2Farticles%2Feav-multi-value-fields%2F' data-shr_title='EAV+multi-value+fields'></a></div><div style="clear: both; min-height: 1px; height: 3px; width: 100%;"></div><!-- End Shareaholic LikeButtonSetTop Automatic --><p>In the <a href="http://www.adaniels.nl/articles/an-alternative-way-of-eav-modeling/">article &#8216;An alternative way of EAV modelling&#8217;</a>, I discussed how to do EAV modelling by casting all values (except text) to integers. I&#8217;ll continue on that and talk about more advanced topics like multi-value fields.</p>
<p><strong>As binary set</strong><br />
Not all questions have only a single option. Some fields we want to represent by radio-buttons, allow the user to select any number of options. For this we can mimic the behaviour of the SET field type of MySQL. A SET is almost similar to an ENUM, except that each bit represents an option. The value can have multiple bits enabled to represent multiple options.</p>
<p>Example: field options for field &#8216;programming language&#8217;</p>
<pre>+-------+-------------+
| value | description |
+-------+-------------+
| 1     | C/C++       |
| 2     | PHP         |
| 4     | Java        |
| 8     | Python      |
| 16    | Ruby        |
+-------+-------------+</pre>
<p>Choosing &#8216;C/C++&#8217;, &#8216;PHP&#8217; and &#8216;Python&#8217; would result in value 11.</p>
<p>There are 2 major disadvantages to this approach. First, with an integer, the number of options is limited to 4*8 = 32. Second, retrieving the description of options would cause joining field on field_option to be done like:</p>
<pre>SELECT value_display(`field_name`, value_display(`field_type`, `value`.`value`, GROUP_CONCAT(`field_option`.`description`), `value`.`text`, `precision`, `date_format`)) FROM `value` INNER JOIN `field` ON `value`.`fid` = `field`.`fid` LEFT JOIN `field_option` ON `value`.`fid` = `field_option`.`fid` AND (IF(`field`.`field_type`='SET, `value`.`value` &amp; `field_option`.`value`, `value`.`value` = `field_option`.`value`)) WHERE `value`.`item_id`=? GROUP BY `value`.`fid`;</pre>
<p>Because of this, the index on the value isn&#8217;t used. Instead, a function has to be performed on each option of the field. This should still give decent enough performance, because this scales linearly and not exponential.</p>
<p><strong>Multiple values</strong><br />
Another way is to store multiple values for the same property. This would mean replacing the primary key from the value table by a normal index.</p>
<pre>CREATE TABLE `value` (
  `item_id` int(10) UNSIGNED NOT NULL DEFAULT '0',
  `fid` int(10) UNSIGNED NOT NULL DEFAULT '0',
  `value` int(11) NOT NULL DEFAULT '0',
  `text` text,
  KEY `item_field`  (`item_id`,`fid`),
  KEY `value` (`fid`,`value`),
  KEY `text` (`text`(255))
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

SELECT value_display(`field_name`, value_display(`field_type`, `value`.`value`, GROUP_CONCAT(`field_option`.`description`), `value`.`text`, `precision`, `date_format`)) FROM `value` INNER JOIN `field` ON `value`.`fid` = `field`.`fid` LEFT JOIN `field_option` ON `value`.`fid` = `field_option`.`fid` AND `value`.`value` = `field_option`.`value` WHERE `value`.`item_id`=? GROUP BY `value`.`fid`;</pre>
<p>The disadvantage of this, is that structural integrity is no longer enforced by the database. Only for a field with the type &#8216;SET&#8217;, multiple values should be allowed. However the database will allow multiple values for other field types (like numeric fields) as well. It is up to the application to replace existing values.</p>
<p><strong>Ranges with a single field</strong><br />
A completely different type of multi-value is ranges. A range has a top and bottom value. We can solve this by saving 2 values in the database. Of the values, the the highest one is always the upper limit and the lowest is the bottom limit.</p>
<pre>delimiter |
CREATE FUNCTION `value_display` (`type` enum('NUMBER', 'ENUM', 'DATE', 'TIME', 'TEXT'), `value` INT, &lt;strong&gt;`max_value` INT&lt;/strong&gt;, `option` VARCHAR(255), `text` TEXT, `precision` INT, `date_format` VARCHAR(50)) RETURNS VARCHAR(255) CHARACTER SET latin1 NO SQL
BEGIN
  // ...
END

SELECT value_display(`field_name`, value_display(`field_type`, MIN(`value`.`value`), IF(COUNT(*)&gt;1, MAX(`value`.`value`, NULL), GROUP_CONCAT(`field_option`.`description`), `value`.`text`, `precision`, `date_format`)) FROM `value` INNER JOIN `field` ON `value`.`fid` = `field`.`fid` LEFT JOIN `field_option` ON `value`.`fid` = `field_option`.`fid` AND `value`.`value` = `field_option`.`value` WHERE `value`.`item_id`=? GROUP BY `value`.`fid`;</pre>
<p><strong>Range by 2 fields</strong><br />
Another was of looking at ranges is that the are simply 2 individual fields, eg `min weight` and `max weight`. When filtering you might want to get all items that have 70 kg in their weight range. You would translate this to `min weight` <= 70 and `max weight` >= 70. (In the example query `min weight` has fid=2 and `max weight` fid=3.)</p>
<pre>SELECT `item`.* FROM `item` INNER JOIN `value` ON `item`.`id`=`value`.`item_id` AND `value`.`fid` IN (2, 3) WHERE (`value`.`fid`=2 AND `value`.`value` &lt;= 70) OR (`value`.`fid`=3 AND `value`.`value` &gt;= 70) HAVING count(*) =2 GROUP BY `item`.`id`;</pre>
<p><strong>Conclusion</strong><br />
We see that using multi-value fields forces to group on fid and generally complicates the queries. If you only need ranges it might be a good idea to use 2 fields instead to keep the queries simpler.</p>
<p><i>The code in this article has not been tested.</i></p>
<div class="shr-publisher-278"></div><!-- Start Shareaholic LikeButtonSetBottom Automatic --><!-- End Shareaholic LikeButtonSetBottom Automatic -->]]></content:encoded>
			<wfw:commentRss>http://www.jasny.net/articles/eav-multi-value-fields/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>How I PHP: multiple inheritance</title>
		<link>http://www.jasny.net/articles/how-i-php-multiple-inheritance/</link>
		<comments>http://www.jasny.net/articles/how-i-php-multiple-inheritance/#comments</comments>
		<pubDate>Sat, 26 Sep 2009 19:38:48 +0000</pubDate>
		<dc:creator>Arnold Daniels</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Ubuntu]]></category>

		<guid isPermaLink="false">http://www.adaniels.nl/?p=242</guid>
		<description><![CDATA[Officially PHP doesn&#8217;t support multiple inheritance. There are several ways around this, without having to duplicate code. PHP 5.4 will support Traits. This concept is almost similar to mixins. For more information check the PHP manual. Wrapper The most commonly used method is to use a wrapper object. 1 2 3 4 5 6 7 <a href='http://www.jasny.net/articles/how-i-php-multiple-inheritance/'>[...]</a>]]></description>
			<content:encoded><![CDATA[<!-- Start Shareaholic LikeButtonSetTop Automatic --><div style="clear: both; min-height: 1px; height: 3px; width: 100%;"></div><div class='shareaholic-like-buttonset' style='float:none;height:30px;'><a class='shareaholic-fblike' data-shr_layout='button_count' data-shr_showfaces='false' data-shr_href='http%3A%2F%2Fwww.jasny.net%2Farticles%2Fhow-i-php-multiple-inheritance%2F' data-shr_title='How+I+PHP%3A+multiple+inheritance'></a><a class='shareaholic-fbsend' data-shr_href='http%3A%2F%2Fwww.jasny.net%2Farticles%2Fhow-i-php-multiple-inheritance%2F'></a><a class='shareaholic-googleplusone' data-shr_size='medium' data-shr_count='true' data-shr_href='http%3A%2F%2Fwww.jasny.net%2Farticles%2Fhow-i-php-multiple-inheritance%2F' data-shr_title='How+I+PHP%3A+multiple+inheritance'></a></div><div style="clear: both; min-height: 1px; height: 3px; width: 100%;"></div><!-- End Shareaholic LikeButtonSetTop Automatic --><p>Officially PHP doesn&#8217;t support multiple inheritance. There are several ways around this, without having to duplicate code.</p>
<p><span style="font-style: italic; color: #21759B">PHP 5.4 will support Traits. This concept is almost similar to mixins. For more information check <a href="http://www.php.net/manual/en/language.oop5.traits.php">the PHP manual</a>.</span></p>
<h2>Wrapper</h2>
<p>The most commonly used method is to use a <a href="http://en.wikipedia.org/wiki/Wrapper_pattern">wrapper</a> object.</p>

<div class="bwp-syntax-block clearfix">
<div class="bwp-syntax-toolbar" style="right: 15px;" ><div class="bwp-syntax-control"><a href="javascript:;" class="bwp-syntax-source-switch" title="View Source Code"></a></div></div>
<div class="bwp-syntax-wrapper clearfix bwp-syntax-simple"style=" height: 252px;"><table class="php"><tbody><tr class="li1"><td class="ln"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
</pre></td><td class="de1"><pre class="de1"><span class="kw2">&lt;?php</span>
&nbsp;
abstract <span class="kw2">class</span> FsNode
<span class="br0">&#123;</span>
&nbsp; <span class="kw2">public</span> <span class="re0">$path</span><span class="sy0">;</span>
&nbsp;
&nbsp; <span class="kw2">public</span> <span class="kw2">function</span> __construct<span class="br0">&#40;</span><span class="re0">$path</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">path</span> <span class="sy0">=</span> <span class="re0">$path</span><span class="sy0">;</span>
&nbsp; <span class="br0">&#125;</span>
&nbsp;
&nbsp; <span class="kw2">public</span> <span class="kw2">function</span> <span class="kw3">rename</span><span class="br0">&#40;</span><span class="re0">$newname</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; <span class="kw3">rename</span><span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">path</span><span class="sy0">,</span> <span class="re0">$newname</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp; &nbsp; <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">path</span> <span class="sy0">=</span> <span class="re0">$newname</span><span class="sy0">;</span>
&nbsp; <span class="br0">&#125;</span>
<span class="br0">&#125;</span>
&nbsp;
<span class="kw2">class</span> <span class="kw3">File</span> <span class="kw2">extends</span> FsNode
<span class="br0">&#123;</span>
&nbsp; <span class="kw2">public</span> <span class="kw2">function</span> getContents<span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; <span class="kw1">return</span> <span class="kw3">file_get_contents</span><span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">path</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp; <span class="br0">&#125;</span>
<span class="br0">&#125;</span>
&nbsp;
<span class="kw2">class</span> <span class="kw3">Dir</span> <span class="kw2">extends</span> FsNode
<span class="br0">&#123;</span>
&nbsp; <span class="kw2">public</span> <span class="kw2">function</span> <span class="kw3">scandir</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; <span class="kw1">return</span> <span class="kw3">scandir</span><span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">path</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp; <span class="br0">&#125;</span>
<span class="br0">&#125;</span>
&nbsp;
<span class="kw2">class</span> <span class="kw3">Symlink</span>
<span class="br0">&#123;</span>
&nbsp; <span class="kw2">protected</span> <span class="re0">$node</span><span class="sy0">;</span>
&nbsp;
&nbsp; <span class="kw2">public</span> <span class="kw2">function</span> __construct<span class="br0">&#40;</span><span class="re0">$node</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">node</span> &nbsp;<span class="sy0">=</span> <span class="re0">$node</span><span class="sy0">;</span>
&nbsp; <span class="br0">&#125;</span>
&nbsp;
&nbsp; <span class="kw2">public</span> <span class="kw2">function</span> target<span class="br0">&#40;</span><span class="re0">$resolve</span><span class="sy0">=</span><span class="kw4">false</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; <span class="kw1">return</span> <span class="re0">$resolve</span> ? <span class="kw3">realpath</span><span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">node</span><span class="sy0">-&gt;</span><span class="me1">path</span><span class="br0">&#41;</span> <span class="sy0">:</span> <span class="kw3">readlink</span><span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">node</span><span class="sy0">-&gt;</span><span class="me1">path</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp; <span class="br0">&#125;</span>
&nbsp;
&nbsp; <span class="kw2">public</span> <span class="kw2">function</span> __call<span class="br0">&#40;</span><span class="re0">$method</span><span class="sy0">,</span> <span class="re0">$args</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; <span class="kw1">return</span> <span class="kw3">call_user_func_array</span><span class="br0">&#40;</span><span class="kw3">array</span><span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">node</span><span class="sy0">,</span> <span class="re0">$method</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="re0">$args</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp; <span class="br0">&#125;</span>
<span class="br0">&#125;</span>
&nbsp;
<span class="re0">$dir</span> <span class="sy0">=</span> <span class="kw2">new</span> <span class="kw3">Dir</span><span class="br0">&#40;</span><span class="st0">&quot;/proc&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$linktodir</span> <span class="sy0">=</span> <span class="kw2">new</span> <span class="kw3">Symlink</span><span class="br0">&#40;</span><span class="kw2">new</span> <span class="kw3">Dir</span><span class="br0">&#40;</span><span class="st0">&quot;/proc/self&quot;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="kw3">var_dump</span><span class="br0">&#40;</span><span class="re0">$linktodir</span><span class="sy0">-&gt;</span><span class="me1">scandir</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span> <span class="co1">// Will be called through __call()</span>
<span class="kw1">echo</span> <span class="re0">$linktodir</span><span class="sy0">-&gt;</span><span class="me1">target</span><span class="br0">&#40;</span><span class="kw4">true</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="st0">&quot;<span class="es1">\n</span>&quot;</span><span class="sy0">;</span></pre></td></tr></tbody></table></div>
<div class="bwp-syntax-source"><pre class="no-parse">&lt;?php

abstract class FsNode
{
  public $path;

  public function __construct($path) {
    $this-&gt;path = $path;
  }

  public function rename($newname) {
    rename($this-&gt;path, $newname);
    $this-&gt;path = $newname;
  }
}

class File extends FsNode
{
  public function getContents() {
    return file_get_contents($this-&gt;path);
  }
}

class Dir extends FsNode
{
  public function scandir() {
    return scandir($this-&gt;path);
  }
}

class Symlink
{
  protected $node;

  public function __construct($node) {
    $this-&gt;node  = $node;
  }

  public function target($resolve=false) {
    return $resolve ? realpath($this-&gt;node-&gt;path) : readlink($this-&gt;node-&gt;path);
  }

  public function __call($method, $args) {
    return call_user_func_array(array($this-&gt;node, $method), $args);
  }
}

$dir = new Dir("/proc");
$linktodir = new Symlink(new Dir("/proc/self"));

var_dump($linktodir-&gt;scandir()); // Will be called through __call()
echo $linktodir-&gt;target(true), "\n";</pre></div></div>

<p>A disadvantage is that is no longer possible to see if a node is a dir by using instanceof. Also, if most of the methods are defined in the wrapped class, this solution will hurt performance.</p>
<h2>Mixin</h2>
<p>A far more interesting approach is to use a <a href="http://en.wikipedia.org/wiki/Mixin">mixins</a>. When you call a non-static method, $this is always passed to that method. This is also the case if the calling object is not inherited from the called class. We can use that to our advantage to do the reverse of the wrapper.</p>

<div class="bwp-syntax-block clearfix">
<div class="bwp-syntax-toolbar" style="right: 15px;" ><div class="bwp-syntax-control"><a href="javascript:;" class="bwp-syntax-source-switch" title="View Source Code"></a></div></div>
<div class="bwp-syntax-wrapper clearfix bwp-syntax-simple"style=" height: 252px;"><table class="php"><tbody><tr class="li1"><td class="ln"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
</pre></td><td class="de1"><pre class="de1">abstract <span class="kw2">class</span> FsNode
<span class="br0">&#123;</span>
&nbsp; <span class="kw2">public</span> <span class="re0">$mixin</span><span class="sy0">;</span>
&nbsp; <span class="kw2">public</span> <span class="re0">$path</span><span class="sy0">;</span>
&nbsp;
&nbsp; <span class="kw2">public</span> <span class="kw2">function</span> __construct<span class="br0">&#40;</span><span class="re0">$path</span><span class="sy0">,</span> <span class="re0">$mixin</span><span class="sy0">=</span><span class="kw4">null</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">path</span> <span class="sy0">=</span> <span class="re0">$path</span><span class="sy0">;</span>
&nbsp; &nbsp; <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">mixin</span> <span class="sy0">=</span> <span class="re0">$mixin</span><span class="sy0">;</span>
&nbsp; <span class="br0">&#125;</span>
&nbsp;
&nbsp; <span class="kw2">function</span> <span class="kw3">rename</span><span class="br0">&#40;</span><span class="re0">$newname</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; <span class="kw3">rename</span><span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">path</span><span class="sy0">,</span> <span class="re0">$newname</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp; &nbsp; <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">path</span> <span class="sy0">=</span> <span class="re0">$newname</span><span class="sy0">;</span>
&nbsp; <span class="br0">&#125;</span>
&nbsp;
&nbsp; <span class="kw2">public</span> &nbsp;<span class="kw2">function</span> __call<span class="br0">&#40;</span><span class="re0">$method</span><span class="sy0">,</span> <span class="re0">$args</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="kw3">isset</span><span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">mixin</span><span class="br0">&#41;</span> <span class="sy0">&amp;&amp;</span> <span class="kw3">ctype_alnum</span><span class="br0">&#40;</span><span class="re0">$method</span><span class="br0">&#41;</span> <span class="sy0">&amp;&amp;</span> <span class="kw3">is_callable</span><span class="br0">&#40;</span><span class="kw3">array</span><span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">mixin</span><span class="sy0">,</span> <span class="re0">$method</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">return</span> <span class="kw3">eval</span><span class="br0">&#40;</span><span class="st0">&quot;return <span class="es4">{$this-&gt;mixin}</span>::<span class="es4">$method</span>(&quot;</span> <span class="sy0">.</span> <span class="br0">&#40;</span><span class="sy0">!</span><span class="kw3">empty</span><span class="br0">&#40;</span><span class="re0">$args</span><span class="br0">&#41;</span> ? <span class="st_h">'$args['</span> <span class="sy0">.</span> <span class="kw3">join</span><span class="br0">&#40;</span><span class="st_h">'], $args['</span><span class="sy0">,</span> <span class="kw3">array_keys</span><span class="br0">&#40;</span><span class="re0">$args</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="sy0">.</span> <span class="st_h">']'</span> <span class="sy0">:</span> <span class="st_h">''</span><span class="br0">&#41;</span> <span class="sy0">.</span> <span class="st0">&quot;);&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp; &nbsp; <span class="br0">&#125;</span>
&nbsp; &nbsp; <span class="kw3">trigger_error</span><span class="br0">&#40;</span><span class="st0">&quot;Call to undefined method &quot;</span> <span class="sy0">.</span> <span class="kw3">get_class</span><span class="br0">&#40;</span><span class="re0">$this</span><span class="br0">&#41;</span> <span class="sy0">.</span> <span class="st0">&quot;::<span class="es4">$method</span>()&quot;</span><span class="sy0">,</span> <span class="kw4">E_USER_ERROR</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp; <span class="br0">&#125;</span>
<span class="br0">&#125;</span>
&nbsp;
<span class="kw2">class</span> <span class="kw3">File</span> <span class="kw2">extends</span> FsNode
<span class="br0">&#123;</span>
&nbsp; <span class="kw2">public</span> <span class="kw2">function</span> getContents<span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; <span class="kw1">return</span> <span class="kw3">file_get_contents</span><span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">path</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp; <span class="br0">&#125;</span>
<span class="br0">&#125;</span>
&nbsp;
<span class="kw2">class</span> <span class="kw3">Dir</span> <span class="kw2">extends</span> FsNode
<span class="br0">&#123;</span>
&nbsp; <span class="kw2">public</span> <span class="kw2">function</span> <span class="kw3">scandir</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; <span class="kw1">return</span> <span class="kw3">scandir</span><span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">path</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp; <span class="br0">&#125;</span>
<span class="br0">&#125;</span>
&nbsp;
<span class="kw2">class</span> <span class="kw3">Symlink</span> <span class="kw2">extends</span> FsNode
<span class="br0">&#123;</span>
&nbsp; <span class="kw2">public</span> <span class="kw2">function</span> target<span class="br0">&#40;</span><span class="re0">$resolve</span><span class="sy0">=</span><span class="kw4">false</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; <span class="kw1">return</span> <span class="re0">$resolve</span> ? <span class="kw3">realpath</span><span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">path</span><span class="br0">&#41;</span> <span class="sy0">:</span> <span class="kw3">readlink</span><span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">path</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp; <span class="br0">&#125;</span>
<span class="br0">&#125;</span>
&nbsp;
<span class="re0">$dir</span> <span class="sy0">=</span> <span class="kw2">new</span> <span class="kw3">Dir</span><span class="br0">&#40;</span><span class="st0">&quot;/proc&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$linktodir</span> <span class="sy0">=</span> <span class="kw2">new</span> <span class="kw3">Dir</span><span class="br0">&#40;</span><span class="st0">&quot;/proc/self&quot;</span><span class="sy0">,</span> <span class="st_h">'Symlink'</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="kw3">var_dump</span><span class="br0">&#40;</span><span class="re0">$linktodir</span><span class="sy0">-&gt;</span><span class="me1">scandir</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw1">echo</span> <span class="re0">$linktodir</span><span class="sy0">-&gt;</span><span class="me1">target</span><span class="br0">&#40;</span><span class="kw4">true</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="st0">&quot;<span class="es1">\n</span>&quot;</span><span class="sy0">;</span> <span class="co1">// Will be called through __call()</span></pre></td></tr></tbody></table></div>
<div class="bwp-syntax-source"><pre class="no-parse">abstract class FsNode
{
  public $mixin;
  public $path;

  public function __construct($path, $mixin=null) {
    $this-&gt;path = $path;
    $this-&gt;mixin = $mixin;
  }

  function rename($newname) {
    rename($this-&gt;path, $newname);
    $this-&gt;path = $newname;
  }

  public  function __call($method, $args) {
    if (isset($this-&gt;mixin) &amp;&amp; ctype_alnum($method) &amp;&amp; is_callable(array($this-&gt;mixin, $method))) {
      return eval("return {$this-&gt;mixin}::$method(" . (!empty($args) ? '$args[' . join('], $args[', array_keys($args)) . ']' : '') . ");");
    }
    trigger_error("Call to undefined method " . get_class($this) . "::$method()", E_USER_ERROR);
  }
}

class File extends FsNode
{
  public function getContents() {
    return file_get_contents($this-&gt;path);
  }
}

class Dir extends FsNode
{
  public function scandir() {
    return scandir($this-&gt;path);
  }
}

class Symlink extends FsNode
{
  public function target($resolve=false) {
    return $resolve ? realpath($this-&gt;path) : readlink($this-&gt;path);
  }
}

$dir = new Dir("/proc");
$linktodir = new Dir("/proc/self", 'Symlink');

var_dump($linktodir-&gt;scandir());
echo $linktodir-&gt;target(true), "\n"; // Will be called through __call()</pre></div></div>

<p><b>caveat:</b> The Symlink class is never instantiated. Properties defined in the Symlink class are ignored. Also, since Symlink doesn&#8217;t extends Dir, it&#8217;s not possible to access protected properties defined in Dir.</p>
<h2>Compile time mixin</h2>
<p>To see if a node is a symlink, you would need to do </p>

<div class="bwp-syntax-block clearfix">
<div class="bwp-syntax-toolbar"><div class="bwp-syntax-control"><a href="javascript:;" class="bwp-syntax-source-switch" title="View Source Code"></a></div></div>
<div class="bwp-syntax-wrapper clearfix bwp-syntax-simple"><table class="php"><tbody><tr class="li1"><td class="ln"><pre class="de1">1
2
3
</pre></td><td class="de1"><pre class="de1"><span class="kw1">if</span> <span class="br0">&#40;</span><span class="kw3">isset</span><span class="br0">&#40;</span><span class="re0">$file</span><span class="sy0">-&gt;</span><span class="me1">mixin</span><span class="br0">&#41;</span> <span class="sy0">&amp;&amp;</span> <span class="br0">&#40;</span><span class="re0">$file</span><span class="sy0">-&gt;</span><span class="me1">mixin</span> <span class="sy0">===</span> <span class="st_h">'Symlink'</span> <span class="sy0">||</span> <span class="kw3">is_subclass_of</span><span class="br0">&#40;</span><span class="re0">$file</span><span class="sy0">-&gt;</span><span class="me1">mixin</span><span class="sy0">,</span> <span class="st_h">'Symlink'</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; <span class="co1">//...</span>
<span class="br0">&#125;</span></pre></td></tr></tbody></table></div>
<div class="bwp-syntax-source"><pre class="no-parse">if (isset($file-&gt;mixin) &amp;&amp; ($file-&gt;mixin === 'Symlink' || is_subclass_of($file-&gt;mixin, 'Symlink'))) {
  //...
}</pre></div></div>

<p>It would be nicer if you could simply use instance of. This is only possible by defining all combination. We can still use mixins though to prevent having to duplicate code.</p>

<div class="bwp-syntax-block clearfix">
<div class="bwp-syntax-toolbar" style="right: 15px;" ><div class="bwp-syntax-control"><a href="javascript:;" class="bwp-syntax-source-switch" title="View Source Code"></a></div></div>
<div class="bwp-syntax-wrapper clearfix bwp-syntax-simple"style=" height: 252px;"><table class="php"><tbody><tr class="li1"><td class="ln"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
</pre></td><td class="de1"><pre class="de1">abstract <span class="kw2">class</span> FsNode
<span class="br0">&#123;</span>
&nbsp; <span class="kw2">protected</span> <span class="re0">$mixin</span><span class="sy0">;</span>
&nbsp; <span class="co1">// Same as above</span>
<span class="br0">&#125;</span>
&nbsp;
<span class="kw2">class</span> <span class="kw3">File</span> <span class="kw2">extends</span> FsNode
<span class="br0">&#123;</span>
&nbsp; <span class="co1">// Same as above</span>
<span class="br0">&#125;</span>
&nbsp;
<span class="kw2">class</span> <span class="kw3">Dir</span> <span class="kw2">extends</span> FsNode
<span class="br0">&#123;</span>
&nbsp; <span class="co1">// Same as above</span>
<span class="br0">&#125;</span>
&nbsp;
<span class="kw2">interface</span> <span class="kw3">Symlink</span>
<span class="br0">&#123;</span><span class="br0">&#125;</span>
&nbsp;
<span class="kw2">class</span> Symlink_Methods <span class="kw2">extends</span> FsNode
<span class="br0">&#123;</span>
&nbsp; <span class="kw2">public</span> <span class="kw2">function</span> target<span class="br0">&#40;</span><span class="re0">$resolve</span><span class="sy0">=</span><span class="kw4">false</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; <span class="kw1">return</span> <span class="re0">$resolve</span> ? <span class="kw3">realpath</span><span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">path</span><span class="br0">&#41;</span> <span class="sy0">:</span> <span class="kw3">readlink</span><span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">path</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp; <span class="br0">&#125;</span>
<span class="br0">&#125;</span>
&nbsp;
<span class="kw2">class</span> SymlinkFile <span class="kw2">extends</span> <span class="kw3">File</span> implements <span class="kw3">Symlink</span>
<span class="br0">&#123;</span>
&nbsp; <span class="kw2">protected</span> <span class="re0">$mixin</span> <span class="sy0">=</span> <span class="st_h">'Symlink_Methods'</span><span class="sy0">;</span>
<span class="br0">&#125;</span>
&nbsp;
<span class="kw2">class</span> SymlinkDir <span class="kw2">extends</span> <span class="kw3">Dir</span> implements <span class="kw3">Symlink</span>
<span class="br0">&#123;</span>
&nbsp; <span class="kw2">protected</span> <span class="re0">$mixin</span> <span class="sy0">=</span> <span class="st_h">'Symlink_Methods'</span><span class="sy0">;</span>
<span class="br0">&#125;</span>
&nbsp;
<span class="re0">$dir</span> <span class="sy0">=</span> <span class="kw2">new</span> <span class="kw3">Dir</span><span class="br0">&#40;</span><span class="st0">&quot;/proc&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$linktodir</span> <span class="sy0">=</span> <span class="kw2">new</span> SymlinkDir<span class="br0">&#40;</span><span class="st0">&quot;/proc/self&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="kw3">var_dump</span><span class="br0">&#40;</span><span class="re0">$linktodir</span><span class="sy0">-&gt;</span><span class="me1">scandir</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw1">echo</span> <span class="re0">$linktodir</span><span class="sy0">-&gt;</span><span class="me1">target</span><span class="br0">&#40;</span><span class="kw4">true</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="st0">&quot;<span class="es1">\n</span>&quot;</span><span class="sy0">;</span> <span class="co1">// Will be called through __call()</span>
&nbsp;
<span class="kw1">if</span> <span class="br0">&#40;</span><span class="re0">$dir</span> instanceof <span class="kw3">Dir</span><span class="br0">&#41;</span> &nbsp;<span class="sy0">;</span> <span class="co1">// True</span>
<span class="kw1">if</span> <span class="br0">&#40;</span><span class="re0">$dir</span> instanceof <span class="kw3">Symlink</span><span class="br0">&#41;</span> &nbsp;<span class="sy0">;</span> <span class="co1">// False</span>
<span class="kw1">if</span> <span class="br0">&#40;</span><span class="re0">$linktodir</span> instanceof <span class="kw3">Dir</span><span class="br0">&#41;</span> &nbsp;<span class="sy0">;</span> <span class="co1">// True</span>
<span class="kw1">if</span> <span class="br0">&#40;</span><span class="re0">$linktodir</span> instanceof <span class="kw3">Symlink</span><span class="br0">&#41;</span> &nbsp;<span class="sy0">;</span> <span class="co1">// True</span></pre></td></tr></tbody></table></div>
<div class="bwp-syntax-source"><pre class="no-parse">abstract class FsNode
{
  protected $mixin;
  // Same as above
}

class File extends FsNode
{
  // Same as above
}

class Dir extends FsNode
{
  // Same as above
}

interface Symlink
{}

class Symlink_Methods extends FsNode
{
  public function target($resolve=false) {
    return $resolve ? realpath($this-&gt;path) : readlink($this-&gt;path);
  }
}

class SymlinkFile extends File implements Symlink
{
  protected $mixin = 'Symlink_Methods';
}

class SymlinkDir extends Dir implements Symlink
{
  protected $mixin = 'Symlink_Methods';
}

$dir = new Dir("/proc");
$linktodir = new SymlinkDir("/proc/self");

var_dump($linktodir-&gt;scandir());
echo $linktodir-&gt;target(true), "\n"; // Will be called through __call()

if ($dir instanceof Dir)  ; // True
if ($dir instanceof Symlink)  ; // False
if ($linktodir instanceof Dir)  ; // True
if ($linktodir instanceof Symlink)  ; // True</pre></div></div>

<div class="shr-publisher-242"></div><!-- Start Shareaholic LikeButtonSetBottom Automatic --><!-- End Shareaholic LikeButtonSetBottom Automatic -->]]></content:encoded>
			<wfw:commentRss>http://www.jasny.net/articles/how-i-php-multiple-inheritance/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>Suhosin patch for PHP 5.3</title>
		<link>http://www.jasny.net/articles/suhosin-patch-for-php-53/</link>
		<comments>http://www.jasny.net/articles/suhosin-patch-for-php-53/#comments</comments>
		<pubDate>Sat, 08 Aug 2009 17:05:15 +0000</pubDate>
		<dc:creator>Arnold Daniels</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Ubuntu]]></category>

		<guid isPermaLink="false">http://blog.adaniels.nl/?p=236</guid>
		<description><![CDATA[The hardened-php project has yet to release a suhosin patch for PHP 5.3.0. We&#8217;re already using PHP 5.3, therefore I&#8217;ve modified the 0.9.7 patch for 5.2.10 to work with 5.3.0. -> Download Suhosin patch for PHP 5.3]]></description>
			<content:encoded><![CDATA[<!-- Start Shareaholic LikeButtonSetTop Automatic --><div style="clear: both; min-height: 1px; height: 3px; width: 100%;"></div><div class='shareaholic-like-buttonset' style='float:none;height:30px;'><a class='shareaholic-fblike' data-shr_layout='button_count' data-shr_showfaces='false' data-shr_href='http%3A%2F%2Fwww.jasny.net%2Farticles%2Fsuhosin-patch-for-php-53%2F' data-shr_title='Suhosin+patch+for+PHP+5.3'></a><a class='shareaholic-fbsend' data-shr_href='http%3A%2F%2Fwww.jasny.net%2Farticles%2Fsuhosin-patch-for-php-53%2F'></a><a class='shareaholic-googleplusone' data-shr_size='medium' data-shr_count='true' data-shr_href='http%3A%2F%2Fwww.jasny.net%2Farticles%2Fsuhosin-patch-for-php-53%2F' data-shr_title='Suhosin+patch+for+PHP+5.3'></a></div><div style="clear: both; min-height: 1px; height: 3px; width: 100%;"></div><!-- End Shareaholic LikeButtonSetTop Automatic --><p>The hardened-php project has yet to release a suhosin patch for PHP 5.3.0. We&#8217;re already using PHP 5.3, therefore I&#8217;ve modified the 0.9.7 patch for 5.2.10 to work with 5.3.0.</p>
<p>-> <a href='http://blog.adaniels.nl/wp-content/uploads/suhosinpatch.zip'>Download Suhosin patch for PHP 5.3</a> <-</p>
<div class="shr-publisher-236"></div><!-- Start Shareaholic LikeButtonSetBottom Automatic --><!-- End Shareaholic LikeButtonSetBottom Automatic -->]]></content:encoded>
			<wfw:commentRss>http://www.jasny.net/articles/suhosin-patch-for-php-53/feed/</wfw:commentRss>
		<slash:comments>31</slash:comments>
		</item>
		<item>
		<title>Hide Gnome Panel</title>
		<link>http://www.jasny.net/articles/hide-gnome-panel/</link>
		<comments>http://www.jasny.net/articles/hide-gnome-panel/#comments</comments>
		<pubDate>Thu, 18 Jun 2009 18:17:18 +0000</pubDate>
		<dc:creator>Arnold Daniels</dc:creator>
				<category><![CDATA[Linux desktop]]></category>

		<guid isPermaLink="false">http://blog.adaniels.nl/?p=215</guid>
		<description><![CDATA[This article shows how to move Gnome Panels to the Compiz widget layer.]]></description>
			<content:encoded><![CDATA[<!-- Start Shareaholic LikeButtonSetTop Automatic --><div style="clear: both; min-height: 1px; height: 3px; width: 100%;"></div><div class='shareaholic-like-buttonset' style='float:none;height:30px;'><a class='shareaholic-fblike' data-shr_layout='button_count' data-shr_showfaces='false' data-shr_href='http%3A%2F%2Fwww.jasny.net%2Farticles%2Fhide-gnome-panel%2F' data-shr_title='Hide+Gnome+Panel'></a><a class='shareaholic-fbsend' data-shr_href='http%3A%2F%2Fwww.jasny.net%2Farticles%2Fhide-gnome-panel%2F'></a><a class='shareaholic-googleplusone' data-shr_size='medium' data-shr_count='true' data-shr_href='http%3A%2F%2Fwww.jasny.net%2Farticles%2Fhide-gnome-panel%2F' data-shr_title='Hide+Gnome+Panel'></a></div><div style="clear: both; min-height: 1px; height: 3px; width: 100%;"></div><!-- End Shareaholic LikeButtonSetTop Automatic --><p>Since a few months I&#8217;ve done away with using the Gnome main menu. Instead I use <a href="http://do.davebsd.com/">Gnome Do</a>. I removed the bottom toolbar long ago, because always use alt-tab. </p>
<p>I&#8217;m not using the top toolbar much either. It was just taking up valuable screen space. I contains only the notification area and a logout button. I was looking at a way to remove it completely. The answer came in the Compiz widget layer. By placing it on the widget layer, fullsize windows actually fill the full screen, but the notification area is still available for applications who need it.</p>
<p>To move Gnome panel to the Widget layer, open &#8216;CompizConfig Settings Manager&#8217; and enable &#8216;Widget Layer&#8217;. Go to tab &#8216;Behaviour&#8217; and add the following text for the &#8216;Widget Windows&#8217; field:</p>
<pre>(class=Gnome-panel &amp; type=Dock)</pre>
<p>The desktop will now be completely clean:<br />
<a href="http://blog.adaniels.nl/wp-content/uploads/desktop-clean.png"><img src="http://blog.adaniels.nl/wp-content/uploads/desktop-clean-300x187.png" alt="Desktop clean" title="desktop-clean" width="300" height="187" class="alignnone size-medium wp-image-217" /></a></p>
<p>With <F9> we can display the widget layer, where the panel is found:<br />
<a href="http://blog.adaniels.nl/wp-content/uploads/desktop-widgets.png"><img src="http://blog.adaniels.nl/wp-content/uploads/desktop-widgets-300x187.png" alt="Desktop Widgets" title="desktop-widgets" width="300" height="187" class="alignnone size-medium wp-image-219" /></a></p>
<p>PS. The widgets you see on the widget layer are <a href="http://www.screenlets.org/index.php/Home">screenlets</a>. Ubuntu has the screenlets package in the universe repository.</p>
<div class="shr-publisher-215"></div><!-- Start Shareaholic LikeButtonSetBottom Automatic --><!-- End Shareaholic LikeButtonSetBottom Automatic -->]]></content:encoded>
			<wfw:commentRss>http://www.jasny.net/articles/hide-gnome-panel/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>How I PHP: How to take a website offline.</title>
		<link>http://www.jasny.net/articles/how-i-php-how-to-take-a-website-offline/</link>
		<comments>http://www.jasny.net/articles/how-i-php-how-to-take-a-website-offline/#comments</comments>
		<pubDate>Wed, 17 Jun 2009 18:48:34 +0000</pubDate>
		<dc:creator>Arnold Daniels</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Ubuntu]]></category>

		<guid isPermaLink="false">http://blog.adaniels.nl/?p=211</guid>
		<description><![CDATA[I&#8217;ve seen a lot of methods used to take a website temporarily off-line for maintenance. Most involve a using PHP to disable the site or renaming the index file. There is however a far better method of doing this, by placing the following in the vhost file or in an .htaccess file in the document <a href='http://www.jasny.net/articles/how-i-php-how-to-take-a-website-offline/'>[...]</a>]]></description>
			<content:encoded><![CDATA[<!-- Start Shareaholic LikeButtonSetTop Automatic --><div style="clear: both; min-height: 1px; height: 3px; width: 100%;"></div><div class='shareaholic-like-buttonset' style='float:none;height:30px;'><a class='shareaholic-fblike' data-shr_layout='button_count' data-shr_showfaces='false' data-shr_href='http%3A%2F%2Fwww.jasny.net%2Farticles%2Fhow-i-php-how-to-take-a-website-offline%2F' data-shr_title='How+I+PHP%3A+How+to+take+a+website+offline.'></a><a class='shareaholic-fbsend' data-shr_href='http%3A%2F%2Fwww.jasny.net%2Farticles%2Fhow-i-php-how-to-take-a-website-offline%2F'></a><a class='shareaholic-googleplusone' data-shr_size='medium' data-shr_count='true' data-shr_href='http%3A%2F%2Fwww.jasny.net%2Farticles%2Fhow-i-php-how-to-take-a-website-offline%2F' data-shr_title='How+I+PHP%3A+How+to+take+a+website+offline.'></a></div><div style="clear: both; min-height: 1px; height: 3px; width: 100%;"></div><!-- End Shareaholic LikeButtonSetTop Automatic --><p>I&#8217;ve seen a lot of methods used to take a website temporarily off-line for maintenance. Most involve a using PHP to disable the site or renaming the index file. There is however a far better method of doing this, by placing the following in the vhost file or in an .htaccess file in the document root:</p>
<pre>Header always set Retry-After "Thu, 18 Jun 2009 08:00:00 +0200"
Redirect 503 /</pre>
<p>This way you are sure no part of the site is used. Also by returning a 503 http response, search-engine crawlers will not reindex your site right at the moment it is down. You can use &#8216;ErrorDocument&#8217; to place a different text than the apache default.</p>
<div class="shr-publisher-211"></div><!-- Start Shareaholic LikeButtonSetBottom Automatic --><!-- End Shareaholic LikeButtonSetBottom Automatic -->]]></content:encoded>
			<wfw:commentRss>http://www.jasny.net/articles/how-i-php-how-to-take-a-website-offline/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>Bye bye MySQL?</title>
		<link>http://www.jasny.net/articles/bye-bye-mysql/</link>
		<comments>http://www.jasny.net/articles/bye-bye-mysql/#comments</comments>
		<pubDate>Mon, 20 Apr 2009 14:08:21 +0000</pubDate>
		<dc:creator>Arnold Daniels</dc:creator>
				<category><![CDATA[MySQL, Sphinx and NoSQL]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://blog.adaniels.nl/?p=194</guid>
		<description><![CDATA[Sun will be bought by Oracle. Will this be the beginning of the end of MySQL? MySQL has a serious market share. For that oracle has already tried to buy MySQL back in 2006. In an interview responding to the offer in 2006, MySQL chief Marten Mickos told that the reason for declining was that <a href='http://www.jasny.net/articles/bye-bye-mysql/'>[...]</a>]]></description>
			<content:encoded><![CDATA[<!-- Start Shareaholic LikeButtonSetTop Automatic --><div style="clear: both; min-height: 1px; height: 3px; width: 100%;"></div><div class='shareaholic-like-buttonset' style='float:none;height:30px;'><a class='shareaholic-fblike' data-shr_layout='button_count' data-shr_showfaces='false' data-shr_href='http%3A%2F%2Fwww.jasny.net%2Farticles%2Fbye-bye-mysql%2F' data-shr_title='Bye+bye+MySQL%3F'></a><a class='shareaholic-fbsend' data-shr_href='http%3A%2F%2Fwww.jasny.net%2Farticles%2Fbye-bye-mysql%2F'></a><a class='shareaholic-googleplusone' data-shr_size='medium' data-shr_count='true' data-shr_href='http%3A%2F%2Fwww.jasny.net%2Farticles%2Fbye-bye-mysql%2F' data-shr_title='Bye+bye+MySQL%3F'></a></div><div style="clear: both; min-height: 1px; height: 3px; width: 100%;"></div><!-- End Shareaholic LikeButtonSetTop Automatic --><p><a href="http://www.sun.com/third-party/global/oracle/index.jsp">Sun will be bought by Oracle</a>. Will this be the beginning of the end of MySQL?</p>
<p><a href="http://www.mysql.com/why-mysql/marketshare">MySQL has a serious market share</a>. For that oracle has already <a href="http://news.cnet.com/2100-7344_3-6040197.html">tried to buy MySQL</a> back in 2006. In an interview responding to the offer in 2006, MySQL chief Marten Mickos told that the reason for declining was that they wanted to keep MySQL an independent product. From that I assume that oracle wanted to merge Oracle DB and MySQL technology. Even though MySQL will be part of a major merger for which the rules aren&#8217;t yet clear, you might think that Oracle hasn&#8217;t changed their ideas about what they want with MySQL in the last 3 years.</p>
<p>Won&#8217;t MySQL just lose most of its market share if it become something else. Other databases like PostgreSQL have been making mayor steps and are in many expects better than MySQL. MySQL has remained to be the only serious open-source RDBMS in respects of market share though. I believe this is mainly because MySQL is known, tried and tested. This might be a fragile thing though.</p>
<p>Based on Oracle&#8217;s decision, I might just take a more serious look at PostgreSQL. Changing is usually not so nice, but change often is.</p>
<p>Any thoughts? Leave a comment or trackback.</p>
<div class="shr-publisher-194"></div><!-- Start Shareaholic LikeButtonSetBottom Automatic --><!-- End Shareaholic LikeButtonSetBottom Automatic -->]]></content:encoded>
			<wfw:commentRss>http://www.jasny.net/articles/bye-bye-mysql/feed/</wfw:commentRss>
		<slash:comments>23</slash:comments>
		</item>
		<item>
		<title>Simple Single Sign-On for PHP (Ajax compatible)</title>
		<link>http://www.jasny.net/articles/simple-single-sign-on-for-php/</link>
		<comments>http://www.jasny.net/articles/simple-single-sign-on-for-php/#comments</comments>
		<pubDate>Sat, 18 Apr 2009 10:24:19 +0000</pubDate>
		<dc:creator>Arnold Daniels</dc:creator>
				<category><![CDATA[Ajax]]></category>
		<category><![CDATA[Authentication]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://blog.adaniels.nl/?p=168</guid>
		<description><![CDATA[This article explains a no-nonse 400 lines of code example of implementing single sign-on (SSO) using PHP. It even works with AJAX.]]></description>
			<content:encoded><![CDATA[<!-- Start Shareaholic LikeButtonSetTop Automatic --><div style="clear: both; min-height: 1px; height: 3px; width: 100%;"></div><div class='shareaholic-like-buttonset' style='float:none;height:30px;'><a class='shareaholic-fblike' data-shr_layout='button_count' data-shr_showfaces='false' data-shr_href='http%3A%2F%2Fwww.jasny.net%2Farticles%2Fsimple-single-sign-on-for-php%2F' data-shr_title='Simple+Single+Sign-On+for+PHP+%28Ajax+compatible%29'></a><a class='shareaholic-fbsend' data-shr_href='http%3A%2F%2Fwww.jasny.net%2Farticles%2Fsimple-single-sign-on-for-php%2F'></a><a class='shareaholic-googleplusone' data-shr_size='medium' data-shr_count='true' data-shr_href='http%3A%2F%2Fwww.jasny.net%2Farticles%2Fsimple-single-sign-on-for-php%2F' data-shr_title='Simple+Single+Sign-On+for+PHP+%28Ajax+compatible%29'></a></div><div style="clear: both; min-height: 1px; height: 3px; width: 100%;"></div><!-- End Shareaholic LikeButtonSetTop Automatic --><p>Associated websites often share user information, so a visitor only has to register once and can use that username and password for all sites. A good example for this is Google. You can use you google account for GMail, Blogger, iGoogle, google code, etc. This is nice, but it would be even nicer if logging in for GMail would mean I&#8217;m also logged in for the other websites. For that you need to implement <a href="http://en.wikipedia.org/wiki/Single_sign-on" target="_blank">single sign-on</a> (SSO).</p>
<p>There are many single sign-on applications and protocols. Most of these are fairly complex. Applications often come with full user management solutions. This makes them difficult to integrate. Most solutions also don&#8217;t work well with AJAX, because redirection is used to let the visitor log in at the SSO server.</p>
<p>I&#8217;ve written a simple single sign-on solution (400 lines of code), which works by linking sessions. This solutions works for normal websites as well as AJAX sites.<br />
<span id="more-168"></span></p>
<h2 id="without_sso">Without SSO</h2>
<p>Let&#8217;s start with a website that doesn&#8217;t have SSO.<br />
<a href="/wp-content/uploads/sso-diagram_no-sso.png"><img src="/wp-content/uploads/sso-diagram_no-sso1-300x252.png" alt="No SSO" title="sso-diagram_no-sso" width="300" height="252" class="size-medium wp-image-171" /></a><br />
The client requests the index page. The page requires that the visitor is logged in. The server creates a new session and sends redirect to the login page. After the visitor has logged in, it displays the index page.</p>
<h2 id="how_it_works">How it works</h2>
<p>When using SSO, when can distinguish 3 parties:</p>
<ul>
<li>Client &#8211; This is the browser of the visitor</li>
<li>Broker &#8211; The website which is visited</li>
<li>Server &#8211; The place that holds the user information</li>
</ul>
<p>The broker will talk to the server in name of the client. For that we want the broker to use the same session as the client. However the client won&#8217;t pass the session id which it has at the server, since it&#8217;s at another domain. Instead the broker will ask the client to pass a token to the server. The server uses the token, in combination with a secret word, to create a session key which is linked session of the client. The broker also know the token and the secret word and can therefore generate the same session key, which it uses to proxy login/logout commands and request info from the server.</p>
<h2 id="first_visit">First visit</h2>
<p><a href="http://sso-alex.jasny.net" target="_blank" style="font-size:14px">-> Try it! (jan/jan1)<-</a><br />
<a href="/wp-content/uploads/sso-diagram_alex.png"><img src="/wp-content/uploads/sso-diagram_alex-280x300.png" alt="" title="sso-diagram_alex" width="280" height="300" class="alignnone size-medium wp-image-175" /></a><br />
When you visit a broker website, it will check to see if a token cookie already exists. It it doesn&#8217;t it, the broker sends a redirect to the server, giving the command to attach sessions and specifying the broker identity, a random token and the originally requested URL. It saves the token in a cookie.</p>
<p>The server will generate a session key based on the broker identity, the secret word of the broker and the token and link this to the session of the client. The session key contains a checksum, so hackers can go out and use random session keys to grab session info. The server redirects the client back to the original URL. After this, the client can talk to the broker, the same way it does when not using SSO.</p>
<p>The client requests the index page at the broker. The page requires that the visitor is logged in. The broker generates the session key, using the token and the secret word, and request the user information at the server. The server responds to the broker that the visitor is not logged. The broker redirects the client to the login page.</p>
<p>The client logs in, sending the username and password to the broker. The broker sends the username and password to the server, again passing the session key. The server returns that login is successful to the broker. The broker redirects the client to the index page. For the index page, the broker will request the user information from the server.</p>
<h2 id="visiting_another_affiliate">Visiting another affiliate</h2>
<p><a href="http://sso-binck.dutchc5.net" target="_blank" style="font-size:14px">-> Try it! <-</a><br />
<a href="/wp-content/uploads/sso-diagram_binck.png"><img src="/wp-content/uploads/sso-diagram_binck-300x238.png" alt="" title="sso-diagram_binck" width="300" height="238" class="alignnone size-medium wp-image-183" /></a><br />
You visit another broker. It also checks for a token cookie. Since each broker is on their own domain, they have different cookies, so no token cookie will be found. The broker will redirect to the server attach to the user session.</p>
<p>The server attaches a session key generated for this broker, which differs from the one for the first broker. It attaches it to the user session. This is the same session the first broker used. The server will redirect back to the original URL.</p>
<p>The client requests the index page at the broker. The broker will request user information from the server. Since the visitor is already logged in, the server returns this information. The index page is shown to the visitor.</p>
<h2 id="using_ajax_rich_internet_application">Using AJAX / Javascript</h2>
<p><a href="http://sso-ajax.jasny.net" target="_blank" style="font-size:14px">-> Try it! <-</a><br />
SSO and AJAX / RIA applications often don&#8217;t go well together. With this type of application, you do not want to leave the page. The application is static and you get the data and do all actions through AJAX. Redirecting an AJAX call to a different website won&#8217;t because of cross-site scripting protection within the browser.</p>
<p>With this solution the client only needs to attach the session by providing the server with a token generated by the broker. That attach request doesn&#8217;t return any information. After attaching the client doesn&#8217;t talk at all to the server any more. Authentication can be done as normal.</p>
<p><a href="/wp-content/uploads/sso-diagram_ajax.png"><img src="/wp-content/uploads/sso-diagram_ajax-241x300.png" alt="" title="sso-diagram_ajax" width="241" height="300" class="alignnone size-medium wp-image-184" /></a><br />
The client check for the token cookie. It it doesn&#8217;t exists, he requests the attach URL from the broker. This attach url includes the broker name and the token, but not a original request URL. The client will open the received url in an &lt;img&gt; and wait until the image is loaded.</p>
<p>The server attaches the browser session key to the user session. When it&#8217;s done it outputs a PNG image. When this image is received by the client, it knows the server has connected the sessions and the broker can be used for authentication. The broker will work as a proxy, passing commands and requests to the sso server and return results to the client.</p>
<h2 id="to_conclude">To conclude</h2>
<p>By connecting sessions, you give the broker the power to act as the client. This method can only be used if you trust all brokers involved. The login information is send through the broker, which he can easily store if the broker has bad intentions.</p>
<p><b><i>Don&#8217;t be square, please share!</i></b></p>
<p><b>Demo</b><br />
<a href="http://sso-alex.jasny.net" target="_blank">Broker &#8216;Alex&#8217;</a><br />
<a href="http://sso-binck.dutchc5.net" target="_blank">Broker &#8216;Binck&#8217;</a><br />
<a href="http://sso-ajax.jasny.net" target="_blank">AJAX broker</a> &#8211; created by <a href="http://uzza.pl/blog/" target="_blank">Lukasz &#8216;Uzza&#8217; Lipinski</a> using <a href="http://ajax.org" target="_blank">Ajax.org PlatForm</a>.</p>
<p>Play around, logging in and out at different brokers. Refresh the other after. Available users:<br />
jan / jan1<br />
peter / peter1<br />
bart / bart1<br />
henk / henk1</p>
<p><b>Download</b><br />
<a href="http://github.com/jasny/SSO/archives/master">Download the code @ github</a><br />
<i>2009-11-16: Updated the software with bugfixes mentioned in comments + alternative for using symlinks.</i></p>
<p>If I overlooked security issues with this SSO implementation, please leave a comment below.</p>
<p>This is a simple implementation of SSO. If you want enterprise stuff have a look at <a href="http://www.novell.com/products/securelogin">Novell Single Sign-On</a>.</p>
<div class="shr-publisher-168"></div><!-- Start Shareaholic LikeButtonSetBottom Automatic --><!-- End Shareaholic LikeButtonSetBottom Automatic -->]]></content:encoded>
			<wfw:commentRss>http://www.jasny.net/articles/simple-single-sign-on-for-php/feed/</wfw:commentRss>
		<slash:comments>197</slash:comments>
		</item>
	</channel>
</rss><!-- WP Super Cache is installed but broken. The path to wp-cache-phase1.php in wp-content/advanced-cache.php must be fixed! -->

