<?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>Tue, 15 May 2012 18:39:00 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.2</generator>
		<item>
		<title>Ant Colony Optimization</title>
		<link>http://noteslog.com/post/ant-colony-optimization/</link>
		<comments>http://noteslog.com/post/ant-colony-optimization/#comments</comments>
		<pubDate>Mon, 02 Apr 2012 12:14:46 +0000</pubDate>
		<dc:creator>Andrea Ercolino</dc:creator>
				<category><![CDATA[Fixing]]></category>

		<guid isPermaLink="false">http://noteslog.com/?p=1642</guid>
		<description><![CDATA[I found this interesting article in Wikipedia about Ant Colony Optimization Algorithms. The basic idea is that collective intelligence can result from a big number of repetitions of very simple efforts. In the case about ants, the paradoxically best features of their simple effort (leave a trace back to food while taking some of it [...]]]></description>
			<content:encoded><![CDATA[<p>I found this interesting article in Wikipedia about <a href="http://en.wikipedia.org/wiki/Ant_colony_optimization_algorithms" target="_blank">Ant Colony Optimization Algorithms</a>. The basic idea is that collective intelligence can result from a big number of repetitions of very simple efforts. In the case about ants, the paradoxically best features of their simple effort (leave a trace back to food while taking some of it back to the nest) is that ants look for food by means of random walks (this guarantees they&#8217;ll find anything in their area) and the trace they leave vanishes after some time (this guarantees they&#8217;ll concentrate on shortest paths from nest to food).</p>
<p>Many crowd based ideas somehow relate to this algorithm, but they are much more complicated than needed. The hope is that if we as humans apply our intelligence to imagine a possibly optimal result we&#8217;ll save time, also collectively. What bothers me is that ants do not need this individual intelligence to find a solution that collectively is optimal. So I&#8217;m wondering if we are not really losing time paying attention to unneeded details.</p>
<ul>
<li><a href="http://en.wikipedia.org/wiki/ESP_Game" target="_blank">ESP Game</a>, <a href="http://video.google.com/videoplay?docid=-8246463980976635143" target="_blank">Human Computation</a>, <a href="http://en.wikipedia.org/wiki/Google_Image_Labeler" target="_blank">Google Image Labeler</a></li>
<li><a href="http://et-model.com/" target="_blank">Energy Transition Model</a></li>
<li><a href="http://www.abc.net.au/catalyst/stories/3296837.htm" target="_blank">Lucrative Algorithms</a>, <a href="http://www.kaggle.com/" target="_blank">Kaggle</a>, <a href="http://www.innocentive.com/" target="_blank">Innocentive</a></li>
<li><a href="http://www.crowdsourcing.org/" target="_blank">Crowd Sourcing</a>, <a href="http://www.kickstarter.com/" target="_blank">Kick Starter</a></li>
<li><a href="http://en.wikipedia.org/wiki/Serious_game" target="_blank">Serious Game</a></li>
</ul>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://noteslog.com/post/ant-colony-optimization/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>About the Barcelona Internet Startups Meetup Group</title>
		<link>http://noteslog.com/post/about-the-barcelona-internet-startups-meetup-group/</link>
		<comments>http://noteslog.com/post/about-the-barcelona-internet-startups-meetup-group/#comments</comments>
		<pubDate>Fri, 30 Mar 2012 08:44:09 +0000</pubDate>
		<dc:creator>Andrea Ercolino</dc:creator>
				<category><![CDATA[Fixing]]></category>

		<guid isPermaLink="false">http://noteslog.com/?p=1561</guid>
		<description><![CDATA[This is a suggestion I posted on 2012-02-27, and I&#8217;m still waiting any feedback. I&#8217;ve been to both Speed Dating meetups as a tech guy. The last time I had the chance to listen to more interesting projects. That&#8217;s good. At the last meetup, I think we were like 20 techies and 60 entrepreneurs. This [...]]]></description>
			<content:encoded><![CDATA[<p>This is a suggestion I posted on 2012-02-27, and I&#8217;m still waiting any feedback.</p>
<blockquote><p>I&#8217;ve been to both Speed Dating meetups as a tech guy. The last time I had the chance to listen to more interesting projects. That&#8217;s good.</p>
<p>At the last meetup, I think we were like 20 techies and 60 entrepreneurs. This is 20 x 60 x 3 minutes each = 3600 minutes of 2 people chats. Those 60 hours could be run with a parallelism of 20 at a time, so they could boil down to a 3 hours meetup at least. In reality, it lasted less than two hours, still enough to meet (theoretically) around 40 different entrepreneurs, but I only met 7 ! (it&#8217;s 10%)</p>
<p>This is not a complain, just a surprise. I know how difficult it is to have people come, so if this group was not successful, there wouldn&#8217;t be such an issue. However it looks like there is space for improvement.</p>
<p>If I went to a speed dating to find a partner to ask out (http://www.youtube.com/watch?v=pYBo5eS5pW8),<br />
1: men and women are the same number<br />
2: age is a big factor which is taken care of beforehand<br />
3: before actually speaking to anyone, I can see how they look like<br />
4: when we meet at last, we have limited time to understand if we match</p>
<p>In our meetups,</p>
<p>1: parallelism. We should be the same number for each role; if we are not, either (a) move extra people to the next meetup or (b) make them play another role.</p>
<p>2: age. IMO, this is the size of the idea (and maturity of the business, if any). It&#8217;s not the same to meet someone who needs a web presence to brand themselves; someone who wants to reach millions of people around the globe; someone who got an idea while having a shower; and someone who is growing the proven business they are expert in.<br />
NOTE that we do not have to filter out members based on this factor, but we should know beforehand the size of their startup effort.</p>
<p>3: likability. This is mostly some details about the idea/team. Who is the leader, who are the other members, what do they do, what are they expert in, what is the idea about, is it selling a product, a service, a software, is there a website to look at, &#8230; And finally, it&#8217;s not the same to be looking for an associate or an employee.</p>
<p>4: matching. If we get here after knowing all of the above, it&#8217;s clear that a lot of time is already saved. This step is not for pitching ! Here we just need to say hello, get a gut feeling about the other and fix any misunderstandings.</p></blockquote>
]]></content:encoded>
			<wfw:commentRss>http://noteslog.com/post/about-the-barcelona-internet-startups-meetup-group/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>How to make the WordPress login for your personal use only</title>
		<link>http://noteslog.com/post/how-to-make-the-wordpress-login-for-your-personal-use-only/</link>
		<comments>http://noteslog.com/post/how-to-make-the-wordpress-login-for-your-personal-use-only/#comments</comments>
		<pubDate>Wed, 08 Feb 2012 18:25:23 +0000</pubDate>
		<dc:creator>Andrea Ercolino</dc:creator>
				<category><![CDATA[Fixing]]></category>
		<category><![CDATA[WordPress]]></category>

		<guid isPermaLink="false">http://noteslog.com/?p=1476</guid>
		<description><![CDATA[Limit Login Attempts is a very good WordPress plugin that limits how many login retries an IP can perform (and fail) before being locked out some time. I cannot recommend it enough: it works like a charm and can be tweaked at will. It also notifies you about lockouts, so that you know if someone is trying [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://devel.kostdoktorn.se/limit-login-attempts" target="_blank">Limit Login Attempts</a> is a very good WordPress plugin that limits how many login retries an IP can perform (and fail) before being locked out some time. I cannot recommend it enough: it works like a charm and can be tweaked at will. It also notifies you about lockouts, so that you know if someone is trying to gain access to your site. Of course you still need a strong password.</p>
<p>&nbsp;</p>
<p>Let&#8217;s see how a login would work in the real world. If the blog was a house, the login page would be the door and the login button the doorbell. A guest comes to the door, slides their business card below, and rings the doorbell. The gatekeeper wakes up, collects the business card, checks guest&#8217;s credentials against the list of people allowed to get in. If there is a match, the gatekeeper opens the door, otherwise goes back to sleep.</p>
<p>In a brute force attack, a guest is trying to get in by submitting ever changing business cards to the gatekeeper, hoping to find a match by mere chance. When such an attack takes place, the gatekeeper is doing a lot of &#8220;useless&#8221; work, and soon they&#8217;ll ask for a raise.</p>
<p>To prevent that, the master provides the gatekeeper with a scanner for business cards that reveals the factory (IP) that made them. Automatically, the scanner rejects any guest whose credentials are written on a business card made by the same factory of the business card of a guest whose credentials were not on the list. This is Limit Login Attempts.</p>
<p>It&#8217;s a nice mechanism that in general will work very well because many guests make their business cards themselves. Additionally there are so many commercial factories around that the risk of a rightful guest to be using the same factory of a gatecrasher is very small.</p>
<p>&nbsp;</p>
<p>Limit Login Attempts has been doing quite a nice job since I installed it, but lately non desirable people have started sliding below the door business cards made by many different factories, thus reducing a bit the effectiveness of the scanner. Along time, I went through all these levels of annoyance.</p>
<ol>
<li>Just ignore them.</li>
<li>Write a sarcastic post about them.</li>
<li>Tweak the scanner to block more and faster.</li>
<li>Destroy the doorbell.</li>
</ol>
<div>I never have guests on my list: it&#8217;s always just me at the door. So I do not need a doorbell because I can call the gatekeeper and trust them to recognize their master&#8217;s voice. Of course, I still present my credentials.</div>
<h4>Step 1 of 2 &#8211; Edit the wp-login.php file</h4>
<p>In your WordPress blog directory there is a file called wp-login.php. It&#8217;s the file that shows the login page. At the very beginning, after the statement that reads <pre><code class="php">require( dirname(__FILE__) . '/wp-load.php' );</code></pre> insert the following lines of PHP code <pre><code class="php">//noteslog.com 2012-02-06 start
$challenge = 'doit'; $answer = 'now';
if (! ('POST' != $_SERVER['REQUEST_METHOD'] || isset($_POST[$challenge]) &amp;&amp; $answer == $_POST[$challenge]))
{
	wp_redirect('http://' . $_SERVER['HTTP_HOST']);
	exit();
}
unset( $challenge, $answer );
//noteslog.com 2012-02-06 end</code></pre></p>
<p>What these lines do is to check if the user has submitted a login form with a given challenge/answer pair. In this example they are <em>doit/now</em>. If the pair is there, then the login form is processed as usual, otherwise the user is redirected to the home page.</p>
<p>Of course you MUST NOT use <em>doit/now</em>, but feel free to choose any other pair of words that you can easily remember, possibly unrelated, like <em>spoon/pig</em>. (sorry, now you cannot use that pair either) Use only letters <em>a</em> through <em>z.</em> (this is not a password !)</p>
<p>That pair is your secret. Do not tell anyone, unless you want them to be able to get through the login page.</p>
<h4>Step 2 of 2 &#8211; Add a bookmarklet to your browser</h4>
<p>Edit the following code such that the challenge/answer pair matches the one used in Step1. Then <a href="http://noteslog.com/post/embeddable/">make a bookmarklet</a> out of it, copy the result in a new bookmark and call it <em>My login</em>. <pre><code class="javascript">(function(){
var action = jQuery('#loginform').attr('action') || '';
if (action.search(/^(http|https):\/\/noteslog\.com\//i) == -1) return alert('No login form or not targeting your blog.');
var challenge = 'doit'; var answer = prompt(challenge, '');
jQuery('#loginform')
  .append('&lt;input type=&quot;hidden&quot; name=&quot;' + challenge + '&quot; value=&quot;' + answer + '&quot;&gt;')
  .submit();
})();</code></pre></p>
<p>What these lines do is to add a challenge/answer pair to the login form and submit it.</p>
<p>When you want to login, go to the login form and enter your username and password as usual, but remember to hit the bookmarklet instead of the standard button. (If you used the standard button you&#8217;d be redirected to the home page, without logging in.)</p>
<h4>A Login Dongle Plugin</h4>
<p>I&#8217;ve already made a Login Dongle plugin for WordPress with all that code. I&#8217;m going to upload it this week. Stay tuned.</p>
<p>EDIT (2012-02-09): <a href="http://wordpress.org/extend/plugins/login-dongle/" target="_blank">http://wordpress.org/extend/<wbr>plugins/login-dongle/</wbr></a></p>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://noteslog.com/post/how-to-make-the-wordpress-login-for-your-personal-use-only/feed/</wfw:commentRss>
		<slash:comments>2</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-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" rel="lightbox[1232]" title="Imagen 1"><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>
	</channel>
</rss>

