<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	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/"
	>

<channel>
	<title>Notes Log &#187; Fixing</title>
	<atom:link href="http://noteslog.com/category/fixing/feed/" rel="self" type="application/rss+xml" />
	<link>http://noteslog.com</link>
	<description></description>
	<lastBuildDate>Fri, 03 Feb 2012 15:11:48 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>How to fix a preg_match bug</title>
		<link>http://noteslog.com/post/how-to-fix-a-preg_match-bug-2/</link>
		<comments>http://noteslog.com/post/how-to-fix-a-preg_match-bug-2/#comments</comments>
		<pubDate>Wed, 03 Aug 2011 11:20:15 +0000</pubDate>
		<dc:creator>Andrea Ercolino</dc:creator>
				<category><![CDATA[Fixing]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://noteslog.com/?p=1445</guid>
		<description><![CDATA[This is the second time I try to fix the preg_match bug with a number 50887. The first time I tried it, less than two months ago, my solution was buggy too, and for this reason I have unpublished it. There are many different scenarios that I didn&#8217;t take into account, but almost all of them [...]]]></description>
			<content:encoded><![CDATA[<p>This is the second time I try to fix the <a href="http://bugs.php.net/bug.php?id=50887" target="_blank">preg_match bug</a> with a number 50887. The <a href="http://noteslog.com/post/how-to-fix-a-preg_match-bug/">first time</a> I tried it, less than two months ago, my solution was buggy too, and for this reason I have unpublished it.</p>
<p>There are many different scenarios that I didn&#8217;t take into account, but almost all of them were related to <a href="http://noteslog.com/post/how-to-count-expected-matches-of-a-php-regular-expression/" target="_blank">How to count expected matches of a PHP regular expression</a>. The rest were essentially the same: I thought that the <a href="http://bugs.php.net/bug.php?id=50887" target="_blank">preg_match</a> bug was affecting just the last optional match. Later I found instead that it affects really all the matches at the end, any number of them, be they optional or not. Due to this <em>little</em> misunderstanding I had to rewrite a big portion of the function.</p>
<p><pre><code class="php">/**
 * Searches $subject for a match to the regular expression given in $pattern.
 * 
 * @see http://bugs.php.net/bug.php?id=50887 This is a fix for that bug
 * 
 * @param string  $pattern 
 *        The pattern to search for, as a string.
 * @param string  $subject 
 *        The input string.
 * @param array   $matches 
 *        If matches is provided, then it is filled with the results of search. 
 *        $matches[0] will contain the text that matched the full pattern, 
 *        $matches[1] will have the text that matched the first captured parenthesized 
 *        subpattern, and so on.
 * @param boolean $flags   
 *        flags can be the following flag: PREG_OFFSET_CAPTURE
 *        If this flag is passed, for every occurring match the appendant string offset 
 *        will also be returned. Note that this changes the value of matches into an 
 *        array where every element is an array consisting of the matched string at 
 *        offset 0 and its string offset into subject at offset 1.
 * @param integer $offset
 *        Normally, the search starts from the beginning of the subject string. The 
 *        optional parameter offset can be used to specify the alternate place from 
 *        which to start the search (in bytes).
 * 
 * @return integer
 *         preg_match() returns the number of times pattern matches. That will be either 
 *         0 times (no match) or 1 time because preg_match() will stop searching after 
 *         the first match. preg_match_all() on the contrary will continue until it 
 *         reaches the end of subject. preg_match() returns FALSE if an error occurred.
 */
function ando_preg_match( $pattern, $subject, array &amp;$matches = NULL, $flags = NULL, $offset = NULL ) 
{ 
    $result = preg_match($pattern, $subject, $matches, $flags, $offset); 
    if (! $result) 
    {
        return $result; 
    }
    $missing = $flags == PREG_OFFSET_CAPTURE ? array('', -1) : '';
    $groups_count = ando_preg_count_groups($pattern, $named_groups, $numbered_groups);
    $matches_count = count($matches);
    $missing_matches = array();
    while ($groups_count != $matches_count - 1 + count($missing_matches))
    { 
        $last_group_offset['named'] = -1;
        $named_groups_count = count($named_groups);
        if ($named_groups_count) 
        { 
            $last_named_group = $named_groups[$named_groups_count - 1];
            $last_group_offset['named'] = $last_named_group[0][1];
            $last_group_name            = $last_named_group[1][0];
        } 
        $last_group_offset['numbered'] = -1; 
        $numbered_groups_count = count($numbered_groups);
        if ($numbered_groups_count) 
        { 
            $last_numbered_group = $numbered_groups[$numbered_groups_count - 1];
            $last_group_offset['numbered'] = $last_numbered_group[0][1]; 
        } 
        $last_group_type = array_search(max($last_group_offset), $last_group_offset);
        $missing_entries = array();
        if ('named' == $last_group_type) 
        { 
            if (! isset($matches[$last_group_name]))  //takes care of dupnames
            {
                $missing_entries[$last_group_name] = $missing;
            }
            $missing_entries[] = $missing;
            array_pop($named_groups);
        }
        else //numbered
        {
            $missing_entries[] = $missing;
            array_pop($numbered_groups);
        }
        $missing_matches = array_merge($missing_entries, $missing_matches);
    }
    $matches = array_merge($matches, $missing_matches);
    return $result; 
}</code></pre></p>
<p>For the function <em>ando_preg_count_groups(&#8230;)</em> see the post <a href="http://noteslog.com/post/how-to-count-expected-matches-of-a-php-regular-expression/" target="_blank">How to count expected matches of a PHP regular expression</a>.</p>
<h4>Minimal Tests</h4>
<p>The following tests are based on <em>$regex = '(?J:(?<DN>Mon|Fri|Sun)(?:day)?|(?<DN>Tue)(?:sday)?|(?<DN>Wed)(?:nesday)?|(?<DN>Thu)(?:rsday)?|(?<DN>Sat)(?:urday)?)';







</em>.</p>
<h5>Test 1a</h5>
<p><pre><code class="php">echo &quot;\n\nIn the next comparison we see the same result, and it really is, because
the count of expected matches equals the count of returned matches.\n&quot;;

preg_match(&quot;@$regex@&quot;, 'This matches on Saturday', $matches);
print_r($matches);

ando_preg_match(&quot;@$regex@&quot;, 'This matches on Saturday', $matches);
print_r($matches);</code></pre></p>
<p><pre>In the next comparison we see the same result, and it really is, because
the count of expected matches equals the count of returned matches.
Array
(
    [0] =&gt; Saturday
    [DN] =&gt; Sat
    [1] =&gt; 
    [2] =&gt; 
    [3] =&gt; 
    [4] =&gt; 
    [5] =&gt; Sat
)
Array
(
    [0] =&gt; Saturday
    [DN] =&gt; Sat
    [1] =&gt; 
    [2] =&gt; 
    [3] =&gt; 
    [4] =&gt; 
    [5] =&gt; Sat
)</pre></p>
<h5>Test 1b</h5>
<p><pre><code class="php">echo &quot;\n\nIn the next comparison we see above that the bug applies to all the matches at the end,
and below that my fix correctly returns all expected matches, including the empty ones.\n&quot;;

preg_match(&quot;@$regex@&quot;, 'This matches on Tuesday', $matches);
print_r($matches);

ando_preg_match(&quot;@$regex@&quot;, 'This matches on Tuesday', $matches);
print_r($matches);</code></pre></p>
<p><pre>In the next comparison we see above that the bug applies to all the matches at the end,
and below that my fix correctly returns all expected matches, including the empty ones.
Array
(
    [0] =&gt; Tuesday
    [DN] =&gt; Tue
    [1] =&gt; 
    [2] =&gt; Tue
)
Array
(
    [0] =&gt; Tuesday
    [DN] =&gt; Tue
    [1] =&gt; 
    [2] =&gt; Tue
    [3] =&gt; 
    [4] =&gt; 
    [5] =&gt; 
)</pre></p>
<h5>Test 1c</h5>
<p><pre><code class="php">echo &quot;\n\nIn the next comparison we see above the bug at its extreme,
and below that my fix works as well as in the other cases.\n&quot;;

preg_match(&quot;@$regex?@&quot;, 'This doesn\'t match on any day but still matches.', $matches);
print_r($matches);

ando_preg_match(&quot;@$regex?@&quot;, 'This doesn\'t match on any day but still matches.', $matches);
print_r($matches);</code></pre></p>
<p><pre>In the next comparison we see above the bug at its extreme,
and below that my fix works as well as in the other cases.
Array
(
    [0] =&gt; 
)
Array
(
    [0] =&gt; 
    [DN] =&gt; 
    [1] =&gt; 
    [2] =&gt; 
    [3] =&gt; 
    [4] =&gt; 
    [5] =&gt; 
)</pre></p>
<p>The following tests are based on <em>$regex = '(?|Saturday|Sun(day)?)';</em>.</p>
<h5>Test 2a</h5>
<p><pre><code class="php">preg_match(&quot;@$regex@&quot;, 'Sun', $matches);
print_r($matches);

ando_preg_match(&quot;@$regex@&quot;, 'Sun', $matches);
print_r($matches);</code></pre></p>
<p><pre>Array
(
    [0] =&gt; Sun
)
Array
(
    [0] =&gt; Sun
    [1] =&gt; 
)</pre></p>
<h5>Test 2b</h5>
<p><pre><code class="php">preg_match(&quot;@$regex@&quot;, 'Sunday', $matches);
print_r($matches);

ando_preg_match(&quot;@$regex@&quot;, 'Sunday', $matches);
print_r($matches);</code></pre></p>
<p><pre>Array
(
    [0] =&gt; Sunday
    [1] =&gt; day
)
Array
(
    [0] =&gt; Sunday
    [1] =&gt; day
)</pre></p>
<h5>Test 2c</h5>
<p><pre><code class="php">preg_match(&quot;@$regex@&quot;, 'Saturday', $matches);
print_r($matches);

ando_preg_match(&quot;@$regex@&quot;, 'Saturday', $matches);
print_r($matches);</code></pre></p>
<p><pre>Array
(
    [0] =&gt; Saturday
)
Array
(
    [0] =&gt; Saturday
    [1] =&gt; 
)</pre></p>
<p>The following tests are based on <em>$regex = '(?|(Sat)ur(day)|Sun(day)?)';</em>.</p>
<h5>Test 3a</h5>
<p><pre><code class="php">preg_match(&quot;@$regex@&quot;, 'Sun', $matches);
print_r($matches);

ando_preg_match(&quot;@$regex@&quot;, 'Sun', $matches);
print_r($matches);</code></pre></p>
<p><pre>Array
(
    [0] =&gt; Sun
)
Array
(
    [0] =&gt; Sun
    [1] =&gt; 
    [2] =&gt; 
)</pre></p>
<h5>Test 3b</h5>
<p><pre><code class="php">preg_match(&quot;@$regex@&quot;, 'Sunday', $matches);
print_r($matches);

ando_preg_match(&quot;@$regex@&quot;, 'Sunday', $matches);
print_r($matches);</code></pre></p>
<p><pre>Array
(
    [0] =&gt; Sunday
    [1] =&gt; day
)
Array
(
    [0] =&gt; Sunday
    [1] =&gt; day
    [2] =&gt; 
)</pre></p>
<h5>Test 3c</h5>
<p><pre><code class="php">preg_match(&quot;@$regex@&quot;, 'Saturday', $matches);
print_r($matches);

ando_preg_match(&quot;@$regex@&quot;, 'Saturday', $matches);
print_r($matches);</code></pre></p>
<p><pre>Array
(
    [0] =&gt; Saturday
    [1] =&gt; Sat
    [2] =&gt; day
)
Array
(
    [0] =&gt; Saturday
    [1] =&gt; Sat
    [2] =&gt; day
)</pre></p>
]]></content:encoded>
			<wfw:commentRss>http://noteslog.com/post/how-to-fix-a-preg_match-bug-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>How to fix a preg_match bug</title>
		<link>http://noteslog.com/post/how-to-fix-a-preg_match-bug/</link>
		<comments>http://noteslog.com/post/how-to-fix-a-preg_match-bug/#comments</comments>
		<pubDate>Sat, 11 Jun 2011 13:37:35 +0000</pubDate>
		<dc:creator>Andrea Ercolino</dc:creator>
				<category><![CDATA[Fixing]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://noteslog.com/?p=1395</guid>
		<description><![CDATA[I wanted to parse the host of a URL with a regular expression to get its third level domain $pattern = '@(?:([^.]+)\.)*[^.]+\.[^.]+@'; Let&#8217;s test the general case with http://www.dnr.state.oh.us/ preg_match($pattern, parse_url('http://www.dnr.state.oh.us/', PHP_URL_HOST), $matches); var_dump($matches); array(2) { [0]=&#62; string(19) &#34;www.dnr.state.oh.us&#34; [1]=&#62; string(5) &#34;state&#34; } Pretty good. And now let&#8217;s test the edge case with http://google.com/ preg_match($pattern, parse_url('http://google.com/', PHP_URL_HOST), $matches); var_dump($matches); [...]]]></description>
			<content:encoded><![CDATA[<p>I wanted to parse the host of a URL with a regular expression to get its third level domain <pre><code class="php">$pattern = '@(?:([^.]+)\.)*[^.]+\.[^.]+@';</code></pre></p>
<p>Let&#8217;s test the general case with http://www.dnr.state.oh.us/ <pre><code class="php">preg_match($pattern, parse_url('http://www.dnr.state.oh.us/', PHP_URL_HOST), $matches);
var_dump($matches);</code></pre></p>
<p><pre>array(2) {
  [0]=&gt;
  string(19) &quot;www.dnr.state.oh.us&quot;
  [1]=&gt;
  string(5) &quot;state&quot;
}</pre></p>
<p>Pretty good. And now let&#8217;s test the edge case with http://google.com/ <pre><code class="php">preg_match($pattern, parse_url('http://google.com/', PHP_URL_HOST), $matches);
var_dump($matches);</code></pre></p>
<p><pre>array(1) {
  [0]=&gt;
  string(10) &quot;google.com&quot;
}</pre></p>
<p>WTF, where is my empty submatch? Since when an optional submatch is not a submatch if it&#8217;s empty?</p>
<p>I googled it and found that there is already a filed <a href="http://bugs.php.net/bug.php?id=50887" target="_blank">bug</a>. The chosen resolution has been <em>won&#8217;t fix</em>!! They say for backward compatibility, but I cannot imagine how fixing it would break anything older.</p>
<ul>
<li>If I expect 3 submatches from my pattern, but I get 2, then I know (for the bug) that the missing submatch is the last one and it&#8217;s an empty string. So I add it myself to the submatches array. Would a programmer do anything different to fix this bug?</li>
<li>If the bug is globally fixed, it means that my old code will always get 3 submatches from that pattern. So my individual fix won&#8217;t get triggered, and having the last submatch the same value (empty string) as the one my fix would have added, I won&#8217;t have any issue, except a bit of (stale) unused code.</li>
</ul>
<h5><span class="Apple-style-span" style="font-size: 13px; font-weight: normal;">To cleanly fix it myself once and for all, I&#8217;ve written a wrapper <em>ando_preg_match</em> that has the same signature and the expected results.</span></h5>
<h5>EDIT: There were some bugs in my own fix to the preg_match bug. For the code, please see the <a href="http://noteslog.com/post/how-to-fix-a-preg_match-bug-2/">new post</a>.</h5>
<p><span style="color: #000000;">In the edge case I get now <pre>array(2) {
  [0]=&gt;
  string(10) &quot;google.com&quot;
  [1]=&gt;
  string(0) &quot;&quot;
}</pre></span></p>
<p><span style="color: #000000;">Unfortunately the wrapper is more complex than I like, but PHP allows regular expressions with named groups and they require a lot of additional code. Anyway I&#8217;ve been able to do it all in a single function that can be easily dropped in any project.</span></p>
<p><span style="color: #000000;">Here is a test with a pattern with named groups, just in case you were wondering what it looks like <pre><code class="php">$pattern = '@(?:(?&lt;subdomain&gt;[^.]+)\.)*[^.]+\.[^.]+@';
ando_preg_match($pattern, parse_url('http://google.com/', PHP_URL_HOST), $matches);
var_dump($matches);</code></pre></span></p>
<p><span style="color: #000000;"><pre>array(3) {
  [0]=&gt;
  string(10) &quot;google.com&quot;
  [&quot;subdomain&quot;]=&gt;
  string(0) &quot;&quot;
  [1]=&gt;
  string(0) &quot;&quot;
}</pre></span></p>
<p><span style="color: #000000;">Actually, this last example allows me to show that my wrapper is really returning the expected result. In fact, just by adding a last non-empty group to the previous pattern, the original and buggy preg_match will work just fine <pre><code class="php">$pattern = '@(?:(?&lt;subdomain&gt;[^.]+)\.)*([^.]+\.[^.]+)@';
preg_match($pattern, parse_url('http://google.com/', PHP_URL_HOST), $matches);
var_dump($matches);</code></pre></span></p>
<p><span style="color: #000000;"><pre>array(4) {
  [0]=&gt;
  string(10) &quot;google.com&quot;
  [&quot;subdomain&quot;]=&gt;
  string(0) &quot;&quot;
  [1]=&gt;
  string(0) &quot;&quot;
  [2]=&gt;
  string(10) &quot;google.com&quot;
}</pre></span></p>
<p><span style="color: #000000;">Of course you&#8217;ll get the same result using the wrapper.</span></p>
]]></content:encoded>
			<wfw:commentRss>http://noteslog.com/post/how-to-fix-a-preg_match-bug/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Mac AppStore forces me to the Spanish site</title>
		<link>http://noteslog.com/post/mac-appstore-forces-me-to-the-spanish-site/</link>
		<comments>http://noteslog.com/post/mac-appstore-forces-me-to-the-spanish-site/#comments</comments>
		<pubDate>Mon, 23 May 2011 17:20:42 +0000</pubDate>
		<dc:creator>Andrea Ercolino</dc:creator>
				<category><![CDATA[Fixing]]></category>

		<guid isPermaLink="false">http://noteslog.com/?p=1356</guid>
		<description><![CDATA[My ID is associated to the Spanish site because my credit card is from there. Now I&#8217;m in Holland but I do not speak Dutch. In the AppStore I can select the country, but not the language!! When I shop for something I want to read reviews about it. Spanish reviews are not available. (most [...]]]></description>
			<content:encoded><![CDATA[<ol>
<li>My ID is associated to the Spanish site because my credit card is from there.</li>
<li>Now I&#8217;m in Holland but I do not speak Dutch.</li>
<li>In the AppStore I can select the country, but not the language!!</li>
<li>When I shop for something I want to read reviews about it.</li>
<li>Spanish reviews are not available. (most of the times)</li>
<li>I select the USA site.</li>
<li>Changing country logs me out!!</li>
<li>Now I have many reviews to browse. (I knew it!!)</li>
<li>If I want to buy something, I click the Buy button.</li>
<li>It wants me to login again, but&#8230;</li>
<li>&#8230;it results in an error. (Translation: this Apple ID can only be used in the Spanish App Store)<br />
<a rel="attachment wp-att-1357" href="http://noteslog.com/post/mac-appstore-forces-me-to-the-spanish-site/screen-shot-2011-05-23-at-19-06-00/" target="_blank"><img title="Screen shot 2011-05-23 at 19.06.00" src="http://noteslog.com/blog/wp-content/uploads/2011/05/Screen-shot-2011-05-23-at-19.06.00-300x94.png" alt="" width="300" height="94" /></a></li>
<li>After accepting the error, I get redirected to the home of the Spanish site.</li>
<li>Now I have to find that app again&#8230;</li>
</ol>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://noteslog.com/post/mac-appstore-forces-me-to-the-spanish-site/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Full UTF-8 support in WordPress</title>
		<link>http://noteslog.com/post/full-utf-8-support-in-wordpress/</link>
		<comments>http://noteslog.com/post/full-utf-8-support-in-wordpress/#comments</comments>
		<pubDate>Fri, 14 Jan 2011 14:36:39 +0000</pubDate>
		<dc:creator>Andrea Ercolino</dc:creator>
				<category><![CDATA[Fixing]]></category>

		<guid isPermaLink="false">http://noteslog.com/?p=1232</guid>
		<description><![CDATA[A few days ago I discovered that WordPress didn&#8217;t support full UTF-8 strings, whose characters are 1 to 4 bytes long. Instead it does support all unicodes belonging to the BMP, whose UTF-8 characters are 1 to 3 bytes long. This WordPress defect is &#8220;caused by&#8221; MySQL 5, which only supports UTF-8 characters in the [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://noteslog.com/post/escaping-and-unescaping-utf-8-characters-in-php/">A few days ago</a> I discovered that WordPress didn&#8217;t support full UTF-8 strings, whose characters are 1 to 4 bytes long. Instead it does support all unicodes belonging to the BMP, whose UTF-8 characters are 1 to 3 bytes long.</p>
<p>This WordPress defect is &#8220;caused by&#8221; MySQL 5, which only supports UTF-8 characters in the BMP. Apparently, MySQL 6 will be full UTF-8 compliant.</p>
<p>This morning, with the help of the UTF-8 class I recently developed, I made up <a href="http://wordpress.org/extend/plugins/full-utf-8/" target="_blank">a new WordPress plugin that adds full UTF-8 support to WordPress</a>.</p>
<p>And this is the same sentence by Douglas Crockford, from the RFC4627 I cited in the previous post:</p>
<blockquote><p>a string containing only the G clef character [<a href="http://www.fileformat.info/info/unicode/char/1d11e/index.htm" target="_blank"><span style="font-size: 2em;">𝄞</span></a>] may be represented as “\uD834\uDD1E”</p></blockquote>
<p><span style="color: #000000;">Windows users see a rectangle: it&#8217;s <span style="color: #ff0000;">a Windows feature</span>, but they should see the following thing</span></p>
<p style="padding-left: 30px;"><span style="color: #000000;"><a href="http://noteslog.com/blog/wp-content/uploads/2011/01/Imagen-11.png"><img class="alignnone size-full wp-image-1270" title="Imagen 1" src="http://noteslog.com/blog/wp-content/uploads/2011/01/Imagen-11.png" alt="" width="595" height="82" /></a></span></p>
<p>You should note that the G clef above (not the one in the picture ;-) appears in the HTML not as an entity but as a common UTF-8 character, entered as is in the WordPress editor. You can see it for yourself by comparing the source code of this post (1) with that of the previous one (2).</p>
<ol>
<li>.<pre><code class="html">&lt;blockquote&gt;&lt;p&gt;a string containing only the G clef character [&lt;a href=&quot;http://www.fileformat.info/info/unicode/char/1d11e/index.htm&quot; target=&quot;_blank&quot;&gt;&lt;span style=&quot;font-size: 2em;&quot;&gt;𝄞&lt;/span&gt;&lt;/a&gt;] may be represented as “\uD834\uDD1E”&lt;/p&gt;&lt;/blockquote&gt;</code></pre></li>
<li>.<pre><code class="html">&lt;blockquote&gt;&lt;p&gt;a string containing only the G clef character [&lt;a href=&quot;http://www.fileformat.info/info/unicode/char/1d11e/index.htm&quot; target=&quot;_blank&quot;&gt;&lt;span style=&quot;font-size: 2em;&quot;&gt;&amp;#119070;&lt;/span&gt;&lt;/a&gt;] may be represented as &amp;#8220;\uD834\uDD1E&amp;#8221;&lt;/p&gt;&lt;/blockquote&gt;</code></pre></li>
</ol>
<p>Note that my plugin works for post and page content, title, excerpt, and also for searches, but it doesn&#8217;t cover custom fields. For this reason, <span style="color: #ff0000;"><span style="color: #000000;">I&#8217;ve just opened <a href="http://core.trac.wordpress.org/ticket/13590" target="_blank">a ticket about this issue in the WordPress Trac</a>:</span> please drop by and comment :-)</span></p>
<p>What follows is the code of my Zend_Utf8 class, which I included in the plugin, after de-Zend-ifying all of it, for safe distribution in the wild.</p>
<p><pre class="ln-1"><code class="php">&lt;?php
/*
Copyright (c) 2011, Andrea Ercolino (http://noteslog.com)
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the &lt;organization&gt; nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS &quot;AS IS&quot; AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL &lt;COPYRIGHT HOLDER&gt; BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/



/**
 * @package    Ando_Utf8
 */
class Ando_Utf8_Exception extends Exception
{}



/**
 * Basic UTF-8 support
 * 
 * @link http://noteslog.com/
 * @link http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
 * 
 * @package    Ando_Utf8
 */
class Ando_Utf8
{
    /**
     * Escape UTF-8 characters using the given options
     * 
     * About the write.callback option
     * -- it receives 
     * -- -- the given write.arguments 
     * -- -- the unicode of the current UTF-8 character
     * -- -- the current (unescaped) UTF-8 character
     * -- it must return the current escaped UTF-8 character
     * 
     * @link http://noteslog.com/post/escaping-and-unescaping-utf-8-characters-in-php/
     * 
     * @param  string $value
     * @param  array $options
     *   'escapeControlChars'   =&gt; boolean (default: TRUE),
     *   'escapePrintableASCII' =&gt; boolean (default: FALSE),
     *   'write'                =&gt; array(
     *       'callback'  =&gt; callable (default: 'sprintf'),
     *       'arguments' =&gt; array    (default: array('\u%04x')),
     *   ),
     *   'extendedUseSurrogate' =&gt; boolean (default: true),
     * 
     * @throws Ando_Utf8_Exception If the code point of any char in $value is 
     *                             not unicode
     * @return string
     */
    public static function escape($value, array $options = array())
    {
        $options = array_merge(array(
            'escapeControlChars'   =&gt; true,
            'escapePrintableASCII' =&gt; false,
            'write'                =&gt; array(
                'callback'  =&gt; 'sprintf',
                'arguments' =&gt; array('\u%04x'),
            ),
            'extendedUseSurrogate' =&gt; true,
        ), $options);
        if (! self::isCallable($options['write']))
        {
            throw new Ando_Utf8_Exception('Expected a valid write handler (callable, array).');
        }
        if (self::validateFilters($options) &amp;&amp; isset($options['filters']['before-write']))
        {
            $value = self::call($options['filters']['before-write'], $value);
        }
        
        $result = &quot;&quot;;
        $length = strlen($value);
        for($i = 0; $i &lt; $length; $i++) {
            $ord_var_c = ord($value[$i]);
            
            switch (true) {
                case ($ord_var_c &lt; 0x20):
                    // code points 0x00000000..0x0000001F, mask 0xxxxxxx
                    $utf8Char = $value[$i];
                    $result .= $options['escapeControlChars']
                        ? self::call($options['write'], array($ord_var_c, $utf8Char))
                        : $value[$i];
                break;

                case ($ord_var_c &lt; 0x80):
                    // code points 0x00000020..0x0000007F, mask 0xxxxxxx
                    $utf8Char = $value[$i];
                    $result .= $options['escapePrintableASCII'] 
                        ? self::call($options['write'], array($ord_var_c, $utf8Char))
                        : $value[$i];
                break;

                case (($ord_var_c &amp; 0xE0) == 0xC0):
                    // code points 0x00000080..0x000007FF, mask 110yyyyy 10xxxxxx
                    $utf8Char = substr($value, $i, 2); $i += 1;
                    $code = self::utf8CharToCodePoint($utf8Char);
                    $result .= self::call($options['write'], array($code, $utf8Char));
                break;

                case (($ord_var_c &amp; 0xF0) == 0xE0):
                    // code points 0x00000800..0x0000FFFF, mask 1110zzzz 10yyyyyy 10xxxxxx
                    $utf8Char = substr($value, $i, 3); $i += 2;
                    $code = self::utf8CharToCodePoint($utf8Char);
                    $result .= self::call($options['write'], array($code, $utf8Char));
                break;

                case (($ord_var_c &amp; 0xF8) == 0xF0):
                    // code points 0x00010000..0x0010FFFF, mask 11110www 10zzzzzz 10yyyyyy 10xxxxxx
                    $utf8Char = substr($value, $i, 4); $i += 3;
                    if ($options['extendedUseSurrogate'])
                    {
                        list($upper, $lower) = self::utf8CharToSurrogatePair($utf8Char);
                        $result .= self::call($options['write'], array($upper, $utf8Char));
                        $result .= self::call($options['write'], array($lower, $utf8Char));
                    }
                    else 
                    {
                        $code = self::utf8CharToCodePoint($utf8Char);
                        $result .= self::call($options['write'], array($code, $utf8Char));
                    }
                break;

                default:
                    //no more cases in unicode, whose range is 0x00000000..0x0010FFFF
                    throw new Ando_Utf8_Exception('Expected a valid UTF-8 character.');
                break;
            }
        }

        return $result;
     }
     
    /**
     * Compute the code point of a given UTF-8 character
     *
     * If available, use the multibye string function mb_convert_encoding
     * TODO reject overlong sequences in $utf8Char
     *
     * @link http://noteslog.com/post/escaping-and-unescaping-utf-8-characters-in-php/
     * 
     * @param  string $utf8Char
     * @throws Ando_Utf8_Exception If the code point of $utf8Char is not unicode
     * @return integer
     */
    public static function utf8CharToCodePoint($utf8Char)
    {
        if (function_exists('mb_convert_encoding')) 
        {
            $utf32Char = mb_convert_encoding($utf8Char, 'UTF-32', 'UTF-8');
        } 
        else 
        {
            $bytes = array('C*');
            list(, $utf8Int) = unpack('N', str_repeat(chr(0), 4 - strlen($utf8Char)) . $utf8Char);
            switch (strlen($utf8Char)) 
            {
                case 1:
                    //Code points U+0000..U+007F
                    //mask   0xxxxxxx (7 bits)
                    //map to 00000000 00000000 00000000 0xxxxxxx
                    $bytes[] = 0;
                    $bytes[] = 0;
                    $bytes[] = 0;
                    $bytes[] = $utf8Int;
                break;
    
                case 2:
                    //Code points U+0080..U+07FF
                    //mask   110yyyyy 10xxxxxx (5 + 6 = 11 bits)
                    //map to 00000000 00000000 00000yyy yyxxxxxx
                    $bytes[] = 0;
                    $bytes[] = 0;
                    $bytes[] = $utf8Int &gt;&gt; 10 &amp; 0x07;
                    $bytes[] = $utf8Int &gt;&gt;  2 &amp; 0xC0 | $utf8Int       &amp; 0x3F;
                break;
    
                case 3:
                    //Code points U+0800..U+D7FF and U+E000..U+FFFF
                    //mask   1110zzzz 10yyyyyy 10xxxxxx (4 + 6 + 6 = 16 bits)
                    //map to 00000000 00000000 zzzzyyyy yyxxxxxx
                    $bytes[] = 0;
                    $bytes[] = 0;
                    $bytes[] = $utf8Int &gt;&gt; 12 &amp; 0xF0 | $utf8Int &gt;&gt; 10 &amp; 0x0F;
                    $bytes[] = $utf8Int &gt;&gt;  2 &amp; 0xC0 | $utf8Int       &amp; 0x3F;
                break;
                             
                case 4:
                    //Code points U+10000..U+10FFFF
                    //mask   11110www 10zzzzzz 10yyyyyy 10xxxxxx (3 + 6 + 6 + 6 = 21 bits)
                    //map to 00000000 000wwwzz zzzzyyyy yyxxxxxx
                    $bytes[] = 0;
                    $bytes[] = $utf8Int &gt;&gt; 22 &amp; 0x1C | $utf8Int &gt;&gt; 20 &amp; 0x03;
                    $bytes[] = $utf8Int &gt;&gt; 12 &amp; 0xF0 | $utf8Int &gt;&gt; 10 &amp; 0x0F;
                    $bytes[] = $utf8Int &gt;&gt;  2 &amp; 0xC0 | $utf8Int       &amp; 0x3F;
                break;
                
                default:
                    //no more cases in unicode, whose range is 0x00000000 - 0x0010FFFF
                    throw new Ando_Utf8_Exception('Expected a valid UTF-8 character.');
                break;
            }
            $utf32Char = call_user_func_array('pack', $bytes);
        }
        list(, $result) = unpack('N', $utf32Char); //unpack returns an array with base 1
        if (0xD800 &lt;= $result &amp;&amp; $result &lt;= 0xDFFF) 
        {
            //reserved for UTF-16 surrogates
            throw new Ando_Utf8_Exception('Expected a valid UTF-8 character.');
        }
        if (0xFFFE == $result || 0xFFFF == $result) 
        {
            //reserved
            throw new Ando_Utf8_Exception('Expected a valid UTF-8 character.');
        }
        
        return $result;
    }
    
    /**
     * Compute the surrogate pair of a given extended UTF-8 character
     * 
     * @link http://noteslog.com/post/escaping-and-unescaping-utf-8-characters-in-php/
     * @link http://en.wikipedia.org/wiki/UTF-16/UCS-2
     * 
     * @param  string $utf8Char
     * @throws Ando_Utf8_Exception If the code point of $utf8Char is not extended unicode
     * @return array
     */
    public static function utf8CharToSurrogatePair($utf8Char) 
    {
        $codePoint = self::utf8CharToCodePoint($utf8Char);
        if ($codePoint &lt; 0x10000) 
        {
            throw new Ando_Utf8_Exception('Expected an extended UTF-8 character.');
        }
        $codePoint -= 0x10000;
        $upperSurrogate = 0xD800 + ($codePoint &gt;&gt; 10);
        $lowerSurrogate = 0xDC00 + ($codePoint &amp; 0x03FF);
        $result = array($upperSurrogate, $lowerSurrogate);
        
        return $result;
    }
    
	/**
     * Unescape UTF-8 characters from a given escape format
     * 
     * About the read.pattern option
     * -- no delimiters and no modifiers allowed
     * -- for back references, your groups start at 3.
     * About the read.callback option
     * -- it receives 
     * -- -- the given read.arguments 
     * -- -- the current match of the pattern with all submatches
     * -- it must return the current unicode integer
     * 
     * @link http://noteslog.com/post/escaping-and-unescaping-utf-8-characters-in-php/
     * 
     * @param  string $value
     * @param  array $options
     *   'read'                 =&gt; array(
     *   	 'pattern'   =&gt; preg     (default: '\\\\u([0-9A-Fa-f]{4})'),
     *       'callback'  =&gt; callable (default: create_function('$all, $code', 'return hexdec($code);')),
     *       'arguments' =&gt; array    (deafult: array()),
     *   ),
     *   'extendedUseSurrogate' =&gt; boolean (default: TRUE),
     * 
     * @throws Ando_Utf8_Exception If the code point of any char in $value is 
     *                             not unicode
     * 
     * @return string
     */
    public static function unescape($value, array $options = array())
    {
        $options = array_merge(array(
        	'read'                 =&gt; array(
            	'pattern'   =&gt; '\\\\u([0-9A-Fa-f]{4})',
                'callback'  =&gt; create_function('$all, $code', 'return hexdec($code);'),
                'arguments' =&gt; array(),
            ),
            'extendedUseSurrogate' =&gt; true,
        ), $options);
        if (! self::isCallable($options['read']))
        {
            throw new Ando_Utf8_Exception('Expected a valid read handler (callable, array).');
        }
        $thereAreFilters = self::validateFilters($options);
        
        $result = &quot;&quot;;
        $length = strlen($value);
        $pattern = '@([\w\W]*?)(' . $options['read']['pattern'] . ')|([\w\W]+)@';
        $offset = 0;
        while (preg_match($pattern, $value, $matches, 0, $offset))
        {
            if (! $matches[2])
            {
                //no more escape patterns
                $result .= $matches[0];
                $offset += strlen($matches[0]);
            }
            else 
            {
                //one more escape pattern
                $result .= $matches[1];
                $offset += strlen($matches[0]);
                $args = array_splice($matches, 2, count($matches) - 1);
                $unicode = self::call($options['read'], $args);//                call_user_func($options['integer'], $matches[2]);
                if ($options['extendedUseSurrogate'] &amp;&amp; (0xD800 &lt;= $unicode &amp;&amp; $unicode &lt; 0xDC00))
                {
                    $upperSurrogate = $unicode;
                    preg_match($pattern, $value, $matches, 0, $offset);
                    if (! $matches[2])
                    {
                        throw new Ando_Utf8_Exception('Expected an extended UTF-8 character.');
                    }
                    $offset += strlen($matches[0]);
                    $args = array_splice($matches, 2, count($matches) - 1);
                    $unicode = self::call($options['read'], $args);//$lowerSurrogate = call_user_func($options['integer'], $matches[2]);
                    $utf8Char = self::utf8CharFromSurrogatePair(array($upperSurrogate, $unicode));
                }
                else 
                {
                    $utf8Char = self::utf8CharFromCodePoint($unicode);
                }
                $result .= $utf8Char;
            }
        }
        if ($thereAreFilters &amp;&amp; isset($options['filters']['after-read']))
        {
            $result = self::call($options['filters']['after-read'], $result);
        }
        
        return $result;
     }
     
    /**
     * Compute the UTF-8 character of a given code point
     *
     * If available, use the multibye string function mb_convert_encoding
     *
     * @link http://noteslog.com/post/escaping-and-unescaping-utf-8-characters-in-php/
     * 
     * @param  integer $codePoint
     * @throws Ando_Utf8_Exception if the code point is not unicode
     * @return string
     */
    public static function utf8CharFromCodePoint($codePoint)
    {
        if (0xD800 &lt;= $codePoint &amp;&amp; $codePoint &lt;= 0xDFFF) 
        {
            //reserved for UTF-16 surrogates
            throw new Ando_Utf8_Exception('Expected a valid code point.');
        }
        if (0xFFFE == $codePoint || 0xFFFF == $codePoint) 
        {
            //reserved
            throw new Ando_Utf8_Exception('Expected a valid code point.');
        }
        
        if (function_exists('mb_convert_encoding')) 
        {
            $utf32Char = pack('N', $codePoint);
            $result = mb_convert_encoding($utf32Char, 'UTF-8', 'UTF-32');
        } 
        else 
        {
            $bytes = array('C*');
            switch (true)
            {
                case ($codePoint &lt; 0x80):
                    //Code points U+0000..U+007F
                    //mask     0xxxxxxx (7 bits)
                    //map from xxxxxxx
                    $bytes[] = $codePoint;
                break;
                
                case ($codePoint &lt; 0x800):
                    //Code points U+0080..U+07FF
                    //mask     110yyyyy 10xxxxxx (5 + 6 = 11 bits)
                    //map from yyy yyxxxxxx
                    $bytes[] = 0xC0 | $codePoint &gt;&gt; 6;
                    $bytes[] = 0x80 | $codePoint       &amp; 0x3F;
                break;
                
                case ($codePoint &lt; 0x10000):
                    //Code points U+0800..U+D7FF and U+E000..U+FFFF
                    //mask     1110zzzz 10yyyyyy 10xxxxxx (4 + 6 + 6 = 16 bits)
                    //map from zzzzyyyy yyxxxxxx
                    $bytes[] = 0xE0 | $codePoint &gt;&gt; 12;
                    $bytes[] = 0x80 | $codePoint &gt;&gt; 6  &amp; 0x3F;
                    $bytes[] = 0x80 | $codePoint       &amp; 0x3F;
                break;
                
                case ($codePoint &lt; 0x110000):
                    //Code points U+10000..U+10FFFF
                    //mask     11110www 10zzzzzz 10yyyyyy 10xxxxxx (3 + 6 + 6 + 6 = 21 bits)
                    //map from wwwzz zzzzyyyy yyxxxxxx
                    $bytes[] = 0xF0 | $codePoint &gt;&gt; 18;
                    $bytes[] = 0x80 | $codePoint &gt;&gt; 12 &amp; 0x3F;
                    $bytes[] = 0x80 | $codePoint &gt;&gt; 6  &amp; 0x3F;
                    $bytes[] = 0x80 | $codePoint       &amp; 0x3F;
                break;
                
                default:
                    throw new Ando_Utf8_Exception('Expected a valid code point.');
                break;
            }
            $result = call_user_func_array('pack', $bytes);
        }
        return $result;
    }
    
    /**
     * Compute the extended UTF-8 character of a given surrogate pair
     * 
     * @link   http://noteslog.com/post/escaping-and-unescaping-utf-8-characters-in-php/
     * @link http://en.wikipedia.org/wiki/UTF-16/UCS-2
     * 
     * @param array $surrogatePair
     * @throws Ando_Utf8_Exception If the surrogate pair is not extended unicode
     * @return string
     */
    public static function utf8CharFromSurrogatePair($surrogatePair) 
    {
        list($upperSurrogate, $lowerSurrogate) = $surrogatePair;
        if (! (0xD800 &lt;= $upperSurrogate &amp;&amp; $upperSurrogate &lt; 0xDC00))
        {
            throw new Ando_Utf8_Exception('Expected an extended UTF-8 character.');
        }
        if (! (0xDC00 &lt;= $lowerSurrogate &amp;&amp; $lowerSurrogate &lt; 0xE000))
        {
            throw new Ando_Utf8_Exception('Expected an extended UTF-8 character.');
        }
        $codePoint = ($upperSurrogate &amp; 0x03FF) &lt;&lt; 10 | ($lowerSurrogate &amp; 0x03FF);
        $codePoint += 0x10000;
        $result = self::utf8CharFromCodePoint($codePoint);
        
        return $result;
    }
    
    /**
     * A little calling interface: validation
     * 
     * @param  array  $handler
     * @return boolean
     */
    private static function isCallable($handler)
    {
        $result = is_callable($handler['callback']) &amp;&amp; is_array($handler['arguments']);
        return $result;
    }
    
    /**
     * A little calling interface: call
     * 
     * @param  array  $handler
     * @param  mixed  $args
     * @return mixed
     */
    private static function call($handler, $args)
    {
        $args = array_merge($handler['arguments'], is_array($args) ? $args : array($args));
        $result = call_user_func_array($handler['callback'], $args);
        return $result;
    }
    
    /**
     * Validate filters. If there are filters return true, else false
     * 
     * @param array $options
     * @throws Ando_Utf8_Exception If there are malformed filters
     * @return boolean
     */
    protected static function validateFilters($options)
    {
        if (isset($options['filters']))
        {
            if (! is_array($options['filters']))
            {
                throw new Ando_Utf8_Exception('Expected valid filters.');
            }
            foreach ($options['filters'] as $key =&gt; $value)
            {
                if (! self::isCallable($value))
                {
                    throw new Ando_Utf8_Exception(&quot;Expected a valid $key handler.&quot;);
                }
            }
            return true;
        }
        return false;
    }
    
}</code></pre></p>
]]></content:encoded>
			<wfw:commentRss>http://noteslog.com/post/full-utf-8-support-in-wordpress/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Open a file by path on Mac OS X</title>
		<link>http://noteslog.com/post/open-a-file-by-path-on-mac-os-x/</link>
		<comments>http://noteslog.com/post/open-a-file-by-path-on-mac-os-x/#comments</comments>
		<pubDate>Fri, 24 Dec 2010 10:37:19 +0000</pubDate>
		<dc:creator>Andrea Ercolino</dc:creator>
				<category><![CDATA[Fixing]]></category>

		<guid isPermaLink="false">http://noteslog.com/?p=945</guid>
		<description><![CDATA[I&#8217;ve been looking for a way to open a deeply nested file in Zend Studio 8 on an iMac. Not an easy task. The best I could do is open a Finder window and press [Shift + Command + G] in the text box enter the file path remove the file portion hit Go drag [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve been looking for a way to open a deeply nested file in Zend Studio 8 on an iMac.</p>
<p>Not an easy task.</p>
<p>The best I could do is</p>
<ol>
<li>open a Finder window and press [Shift + Command + G]</li>
<li>in the text box enter the file path</li>
<li>remove the file portion</li>
<li>hit Go</li>
<li>drag and drop the file from the Finder window to the Zend Studio window</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://noteslog.com/post/open-a-file-by-path-on-mac-os-x/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>How to add events to a PHP project</title>
		<link>http://noteslog.com/post/how-to-add-events-to-a-php-project/</link>
		<comments>http://noteslog.com/post/how-to-add-events-to-a-php-project/#comments</comments>
		<pubDate>Sun, 28 Jun 2009 21:20:09 +0000</pubDate>
		<dc:creator>Andrea Ercolino</dc:creator>
				<category><![CDATA[Fixing]]></category>

		<guid isPermaLink="false">http://noteslog.com/?p=383</guid>
		<description><![CDATA[Here is a short class that will let you add events to a PHP project. The class EventManager defines a singleton, which you access with the EventManager::getInstance() expression. Here is the logic: bind a handler to an event: EventManager::getInstance()-&#62;bind( &#8216;event&#8217;, &#8216;handler&#8217; ) An event can be a string or a PHP regular expression. A handler must be a [...]]]></description>
			<content:encoded><![CDATA[<p>Here is a short class that will let you add events to a PHP project.</p>
<p>The class EventManager defines a singleton, which you access with the EventManager::getInstance() expression.</p>
<p><span id="more-383"></span>Here is the logic:</p>
<ol>
<li>bind a handler to an event: EventManager::getInstance()-&gt;bind( &#8216;event&#8217;, &#8216;handler&#8217; )<br />
An event can be a string or a PHP regular expression. A handler must be a PHP callable expression.</li>
<li>trigger an event: EventManager::getInstance()-&gt;trigger( &#8216;event&#8217; )<br />
A triggered event should be  a string. That string is going to be matched against all the bound events. Whenever a match is found, all the handlers bound to the matching events will be called. All the arguments passed to trigger will be passed along to the handlers. If a handler returns a false value, all other handlers won&#8217;t be called.</li>
</ol>
<p><pre><code class="php">class EventManager {
    
    /**
     * Singleton setup
     */
	static protected $_instance = null;
	protected function __construct() {}
	protected function __clone() {}
	static public function getInstance() {
		if (is_null( self::$_instance )) {
		    $class = __CLASS__;
			self::$_instance = new $class();
		}
		return self::$_instance;
	}
	
	
	/**
	 * Stores bindings between events and their handlers
	 *
	 * @var array
	 */
	protected $_bindings = array();
	
	
	/**
	 * Returns the id of the given $handler
	 *
	 * @param $handler a callable expression
	 * @return string
	 */
	protected function getId( $handler ) {
	    if (! is_callable( $handler )) {
	        throw new Exception('Expected a callable expression');
	    }
	    if (is_string( $handler )) {
	        $id = &quot;function: $handler&quot;;
	    } else {
	        $container = $hadler[0];
	        $method = $handler[1];
	        if (is_string( $container )) {
	            $class = $container;
	            $id = &quot;class: $class, method: $method&quot;;
	        } else {
	            $class = get_class( $container );
	            $object = spl_object_hash( $container );
	            $id = &quot;class: $class, object: $object, method: $method&quot;;
	        }
	    }
	    return $id;
	}
	
	
	/**
	 * Returns TRUE if the given $expression is a regular expression in PHP
	 * TODO improve EventManager::isRegExp by allowing matching delimiters (), [], {}, &lt;&gt;
	 *
	 * @param $regex
	 * @return boolean
	 */
	protected function isRegExp( $expression ) {
	    if (is_string( $expression ) &amp;&amp; preg_match( '/([^\w \\])[^\1\\\r\n]*(?:\\.[^\1\\\r\n]*)*\1/', $expression )) {
	        return true;
	    }
	    return false;
	}
	
	
	/**
	 * Binds a $handler to an $event
	 *
	 * @param string $event
	 * @param callable $handler
	 * @return EventManager
	 */
	public function bind( $event, $handler ) {
	    $id = $this-&gt;getId( $handler );
	    $this-&gt;_bindings[ $event ][ $id ] = $handler;
		return $this;
	}
	
	
	/**
	 * Unbinds a $handler from an $event
	 *
	 * @param string $event
	 * @param callable $handler
	 * @return EventManager
	 */
	public function unbind( $event, $handler = null ) {
		if (is_null( $handler )) {
			unset( $this-&gt;_bindings[ $event ] );
		} else {
		    $id = $this-&gt;getId( $handler );
    	    unset( $this-&gt;_bindings[ $event ][ $id ] );
		}
		return $this;
	}
	
	
	/**
	 * Dispatches a $triggered event to all its matching handlers
	 *
	 * @param string $triggered
	 * @return EventManager
	 */
	public function trigger( $triggered ) {
	    $result = true;
	    $args = func_get_args();
	    foreach ($this-&gt;_bindings as $event =&gt; $handlers) {
	        if ($this-&gt;isRegExp( $event )) {
    	        if (! preg_match( $event, $triggered )) {
    	            continue;
    	        }
	        } else {
	            if ($event != $triggered) {
	                continue;
	            }
	        }
    	    if (is_array( $handlers )) {
    			foreach ($handlers as $handler) {
    			    if (is_callable( $handler )) {
    			        $result = call_user_func_array( $handler, $args );
        				if (false === $result) {
        					break;
        				}
    			    }
    			}
    		}
    		if (false === $result) {
				break;
			}
	    }
		return $this;
	}
	
}</code></pre></p>
]]></content:encoded>
			<wfw:commentRss>http://noteslog.com/post/how-to-add-events-to-a-php-project/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>null is null or is not an object</title>
		<link>http://noteslog.com/post/null-is-null-or-is-not-an-object/</link>
		<comments>http://noteslog.com/post/null-is-null-or-is-not-an-object/#comments</comments>
		<pubDate>Wed, 29 Apr 2009 19:11:11 +0000</pubDate>
		<dc:creator>Andrea Ercolino</dc:creator>
				<category><![CDATA[Fixing]]></category>

		<guid isPermaLink="false">http://noteslog.com/?p=233</guid>
		<description><![CDATA[I stumbled upon this javascript error in IE7 today. Shouldn&#8217;t it be null is null and is not an object ?]]></description>
			<content:encoded><![CDATA[<p>I stumbled upon this javascript error in IE7 today.</p>
<p>Shouldn&#8217;t it be <strong>null is null and is not an object</strong> ?</p>
]]></content:encoded>
			<wfw:commentRss>http://noteslog.com/post/null-is-null-or-is-not-an-object/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>How to show a slow loading page</title>
		<link>http://noteslog.com/post/how-to-show-a-slow-loading-page/</link>
		<comments>http://noteslog.com/post/how-to-show-a-slow-loading-page/#comments</comments>
		<pubDate>Sun, 05 Apr 2009 22:52:59 +0000</pubDate>
		<dc:creator>Andrea Ercolino</dc:creator>
				<category><![CDATA[Fixing]]></category>
		<category><![CDATA[HTML]]></category>
		<category><![CDATA[jQuery]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://noteslog.com/?p=343</guid>
		<description><![CDATA[Problem &#8211; Slow loading pages are frustrating, for web users and developers too. Of course there are many possible tweaks you can use to speed them up, but sometimes you cannot optimize a page any further. UPDATED 2009-10-11 Solution &#8211; If you can&#8217;t beat them, join them. You can show a progress indicator to let your [...]]]></description>
			<content:encoded><![CDATA[<p><strong>Problem</strong> &#8211; Slow loading pages are frustrating, for web users and developers too. Of course there are many possible tweaks you can use to speed them up, but sometimes you cannot optimize a page any further.</p>
<ul>
<li>UPDATED 2009-10-11</li>
</ul>
<p><span id="more-343"></span></p>
<p><strong>Solution</strong> &#8211; If you can&#8217;t beat them, join them. You can show a progress indicator to let your web users know that your server is thinking hard the answer to their request, but how do you do that?</p>
<p><strong>Requirement 1</strong> &#8211; You cannot put the progress indicator on the leaving page, because you don&#8217;t want to modify every single reference to a slow page. It doesn&#8217;t make sense either: a slow page is slow by itself! You really don&#8217;t need that coupling.</p>
<p><strong>Requirement 2</strong> - You cannot put the progress indicator on the landing page, the slow one, because the browser needs it before the slow page has arrived. If they arrive together, the progress indicator is useless!</p>
<p>There is a <a href="http://www.aspnetpro.com/NewsletterArticle/2003/08/asp200308bm_l/asp200308bm_l.asp" target="_blank">good article</a> about this issue but it fails at implementing the first requirement. Anyway that was the idea I worked on when starting this project.</p>
<p>Here is the waiting page that get&#8217;s loaded while loading the slow page. For browsers other than IE, the trick is to change the document.location.href property after loading the <a href="http://ajaxload.info/" target="_blank">progress indicator</a> image. In IE we change the document.location.href and then add the image.</p>
<p>I used jQuery to make it simple.</p>
<p><strong>waiting.php</strong></p>
<p><pre class="chili-all"><code class="html">&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Transitional//EN&quot; &quot;DTD/xhtml1-transitional.dtd&quot;&gt;

&lt;html xml:lang=&quot;en&quot; lang=&quot;en&quot; xmlns=&quot;http://www.w3.org/1999/xhtml&quot;&gt;&lt;head&gt;

&lt;title&gt;Loading Page...&lt;/title&gt;

&lt;script type=&quot;text/javascript&quot; src=&quot;http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot;&gt;</code><code class="javascript">
jQuery( function( $ ) {
	function redirect() {
		$('form').submit();
	}
	
	if($.browser.msie) {
		redirect();
		$('img').appendTo('body').attr('src', 'loading-page.gif');
	} else {
		$('#loading').bind('load', function() {
	        redirect();
	    }).attr('src', 'loading-page.gif');
	}
} );
</code><code class="html">&lt;/script&gt;

&lt;/head&gt;&lt;body&gt;

&lt;h1&gt;Loading Page...&lt;/h1&gt;
&lt;img id=&quot;loading&quot; /&gt;
&lt;form method=&quot;&lt;?php </code><code class="php">echo $method </code><code class="html">?&gt;&quot; action=&quot;&lt;?php </code><code class="php">echo $url </code><code class="html">?&gt;&quot;&gt;
 &lt;input type=&quot;hidden&quot; name=&quot;waiting&quot; value=&quot;&lt;?php </code><code class="php">echo $waiting </code><code class="html">?&gt;&quot; /&gt;
&lt;/form&gt;

&lt;/body&gt;&lt;/html&gt;</code></pre>


</p>
<p>And here is the slow page.</p>
<p><strong>slow-page.php</strong></p>
<p><pre class="chili-all"><code class="html">&lt;?php
</code><code class="php">
if ( ! isset($_REQUEST['waiting']) ) {
    $url = htmlspecialchars( $_SERVER['REQUEST_URI'] );
	$method = $_SERVER['REQUEST_METHOD'];
	$waiting = time();
	include &quot;waiting.php&quot;;
    return;
}

sleep(10); //simulate a slow task

</code><code class="html">?&gt;

&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Transitional//EN&quot; &quot;DTD/xhtml1-transitional.dtd&quot;&gt;

&lt;html xml:lang=&quot;en&quot; lang=&quot;en&quot; xmlns=&quot;http://www.w3.org/1999/xhtml&quot;&gt;&lt;head&gt;
 &lt;title&gt;Slow Page&lt;/title&gt;
&lt;/head&gt;&lt;body&gt;
 &lt;h1&gt;Hello World!&lt;/h1&gt;
&lt;/body&gt;&lt;/html&gt;</code></pre>


</p>
<p>You can test it <a href="http://noteslog.com/personal/projects/page-loading/3/slow-page.php" target="_blank">here</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://noteslog.com/post/how-to-show-a-slow-loading-page/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Your PHP MySQL library version 5.0.51a differs from your MySQL server version 5.1.30</title>
		<link>http://noteslog.com/post/your-php-mysql-library-version/</link>
		<comments>http://noteslog.com/post/your-php-mysql-library-version/#comments</comments>
		<pubDate>Sat, 07 Feb 2009 21:31:30 +0000</pubDate>
		<dc:creator>Andrea Ercolino</dc:creator>
				<category><![CDATA[Fixing]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[WampServer]]></category>

		<guid isPermaLink="false">http://noteslog.com/?p=332</guid>
		<description><![CDATA[After installing WampServer 2.0f, the also installed phpMyAdmin 3.1.1 displays a notice at the bottom of the main page, saying: &#8220;Your PHP MySQL library version 5.0.51a differs from your MySQL server version 5.1.30&#8243;. I was experimenting some issues with Toad for MySQL, totally unrelated by the way and still to be solved, so I was [...]]]></description>
			<content:encoded><![CDATA[<p>After installing WampServer 2.0f, the also installed phpMyAdmin 3.1.1 displays a notice at the bottom of the main page, saying: &#8220;Your PHP MySQL library version 5.0.51a differs from your MySQL server version 5.1.30&#8243;.</p>
<p><span id="more-332"></span></p>
<p>I was experimenting some issues with Toad for MySQL, totally unrelated by the way and still to be solved, so I was sensitive to malfunctions, and decided to go through it.</p>
<p>The problem is that WampServer 2.0f installs MySQL 5.1.30 and PHP 5.2.8, which only supports MySQL 5.0.51a. So the only available solution, if you  want to stay with the last stable PHP version, is to downgrade MySQL to version 5.0.51a.</p>
<p>You can download and install a <a href="http://www.wampserver.com/en/addons_mysql.php" target="_blank">MySQL add on</a>, by using the menu option WampServer/MySQL/Version/Get more&#8230; Unfortunately, the installation does not end when the installer finishes. In fact you&#8217;ll need to manually configure your new database server.</p>
<p>Switch to it by using the menu option WampServer/MySQL/Version/5.0.51a, wait for the WampServer status icon to get back to white, and reload phpMyAdmin. Works? Mine didn&#8217;t. The problem is that the new MySQL server is still not configured and very empty.</p>
<ul>
<li>Switch back to the first server (5.1.30) and  export all your databases (don&#8217;t export information_schema nor mysql). Then switch to the second server (5.0.51a) for the last time.</li>
<li>Edit the file wamp/apps/phpmyadmin3.1.1/config.inc.php and remove the password.</li>
<li>Delete cookies and cache of the browser. This should be enough for allowing phpMyAdmin to load again.</li>
<li>Go to the Priviliges tab, and remove the users rows with production.mysql.com in them: they are garbage of this MySQL version.</li>
<li>Edit the user root/localhost and set a password.</li>
<li>Reload phpMyAdmin and check that it fails.</li>
<li>Edit again the file wamp/apps/phpmyadmin3.1.1/config.inc.php and insert the password.</li>
<li>Reload phpMyAdmin and check that it doesn&#8217;t fail.</li>
<li>Add new users/databases as needed.</li>
<li>Select each database and import its schema and data, from your previous exports.</li>
</ul>
<p>Done.</p>
]]></content:encoded>
			<wfw:commentRss>http://noteslog.com/post/your-php-mysql-library-version/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>How to cause a view refresh in jQuery</title>
		<link>http://noteslog.com/post/how-to-cause-a-view-refresh-in-jquery/</link>
		<comments>http://noteslog.com/post/how-to-cause-a-view-refresh-in-jquery/#comments</comments>
		<pubDate>Sat, 05 Jul 2008 13:05:57 +0000</pubDate>
		<dc:creator>Andrea Ercolino</dc:creator>
				<category><![CDATA[Fixing]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://noteslog.com/?p=216</guid>
		<description><![CDATA[$('body').width( $('body').width() - 1 ).width( $('body').width() + 1 );]]></description>
			<content:encoded><![CDATA[<p><pre><code class="javascript">$('body').width( $('body').width() - 1 ).width( $('body').width() + 1 );</code></pre></p>
]]></content:encoded>
			<wfw:commentRss>http://noteslog.com/post/how-to-cause-a-view-refresh-in-jquery/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

