<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/">
  <channel>
    <title>Blog entries tagged spl :: mwop.net</title>
    <description>Blog entries tagged spl :: mwop.net</description>
    <pubDate>Fri, 21 Jan 2011 17:07:25 -0600</pubDate>
    <generator>Laminas_Feed_Writer 2 (https://getlaminas.org)</generator>
    <link>https://mwop.net/blog/tag/spl</link>
    <atom:link rel="self" type="application/rss+xml" href="https://mwop.net/blog/tag/spl/rss.xml"/>
    <item>
      <title>Taming SplPriorityQueue</title>
      <pubDate>Fri, 21 Jan 2011 17:07:25 -0600</pubDate>
      <link>https://mwop.net/blog/253-Taming-SplPriorityQueue.html</link>
      <guid>https://mwop.net/blog/253-Taming-SplPriorityQueue.html</guid>
      <author>contact@mwop.net (Matthew Weier O'Phinney)</author>
      <dc:creator>Matthew Weier O'Phinney</dc:creator>
      <content:encoded><![CDATA[<p><a href="http://php.net/SplPriorityQueue">SplPriorityQueue</a> is a fantastic new feature
of PHP 5.3. However, in trying to utilize it in a few projects recently, I've
run into some behavior that's (a) non-intuitive, and (b) in some cases at
least, undesired. In this post, I'll present my solutions.</p>


<h2>Of Heaps and Queues</h2>
<p><em>Queues</em> in programming are any data structure that, when iterated, return
values in a &quot;first-in-first-out&quot; (FIFO) order. For &quot;last-in-first-out&quot; (LIFO)
iteration, you define a <em>stack</em>.</p>
<p>A <em>heap</em> is a data structure where, given a specific node, all nodes beneath it
are of a value less than it. (Technically, this would be considered a
&quot;max-heap,&quot; as you can also have a variant where all child nodes are of a value
greater; this is called a &quot;min-heap.&quot;)</p>
<p>A <em>priority queue</em> is a specialized version of a max-heap. Typically, data is
registered with a specific priority — so the max-heap is looking at only the
priority value, not the data itself. This allows inserting data into the queue
in any order desired, while ensuring that they are iterated in the order
specified by the priorities provided.</p>
<p>PHP offers SPL data structures corresponding to each:</p>
<ul>
<li><a href="http://php.net/SplQueue">SplQueue</a>, corresponding to a <em>queue</em>.</li>
<li><a href="http://php.net/SplStack">SplStack</a>, corresponding to a <em>stack</em>.</li>
<li><a href="http://php.net/SplHeap">SplHeap</a>, corresponding to a <em>heap</em>.</li>
<li><a href="http://php.net/SplMaxHeap">SplMaxHeap</a>, correspondaxg to a <em>max-heap</em>.</li>
<li><a href="http://php.net/SplMinHeap">SplMinHeap</a>, corresponding to a <em>min-heap</em>.</li>
<li><a href="http://php.net/SplPriorityQueue">SplPriorityQueue</a>, corresponding to a <em>priority queue</em>.</li>
</ul>
<h2>Problems</h2>
<p>The first problem I ran into was really a lapse of reasoning on my part, and is
namely this:</p>
<blockquote>
<p><em>Iterating over a heap removes the values from the heap.</em></p>
</blockquote>
<p>Basically, in order to satisfy the <em>heap</em> contract, which is that the root node
is always the maximum value (or minimum, in the case of a min-heap), any
previous nodes must be removed.</p>
<p>The problem with this, obviously, is that if you want to iterate over a heap of
any sort multiple times, well, you can't with the same instance.</p>
<p>The next problem I ran into was with SplPriorityQueue specifically: when items
of equal priority are enqueued, the iteration order of these items is…
unexpected. While the <a href="http://php.net/splpriorityqueue.compare">documentation</a>
notes that &quot;multiple elements with the same priority will get dequeued in no
particular order,&quot; the fact is that it <em>is</em> predictable, and unintuitive. For
example, given the following:</p>
<pre><code class="language-php hljs php" data-lang="php">$queue-&gt;insert(<span class="hljs-string">'foo'</span>, <span class="hljs-number">1000</span>);
$queue-&gt;insert(<span class="hljs-string">'bar'</span>, <span class="hljs-number">1000</span>);
$queue-&gt;insert(<span class="hljs-string">'baz'</span>, <span class="hljs-number">1000</span>);
$queue-&gt;insert(<span class="hljs-string">'bat'</span>, <span class="hljs-number">1000</span>);

<span class="hljs-keyword">foreach</span> ($queue <span class="hljs-keyword">as</span> $data) <span class="hljs-keyword">echo</span> $data, <span class="hljs-string">" "</span>;
</code></pre>
<p>I'd expect a result of &quot;foo bar baz bat&quot;, assuming FIFO order (which is
expected in a <em>queue</em>) for equal priorities; &quot;foo baz bat bar&quot;, assuming
ordering by data (which might be expected in a max-heap). In fact, neither is
true: the first item will be emitted first, and then the remaining items in
reverse order of when enqueued: &quot;foo bat baz bar&quot;.</p>
<p>While this may be somewhat predictable, I find I don't want to assume such
order, nor try and write code around it.</p>
<h2>Solutions</h2>
<h3>Allowing multiple iterations</h3>
<p>Allowing multiple iterations of a queue is as easy as cloning it prior to
iteration:</p>
<pre><code class="language-php hljs php" data-lang="php"><span class="hljs-keyword">foreach</span> (<span class="hljs-keyword">clone</span> $queue <span class="hljs-keyword">as</span> $datum) <span class="hljs-keyword">echo</span> $datum, <span class="hljs-string">" "</span>;
</code></pre>
<p>The problem is automating this — there are cases where I don't want users to
really have to understand the internal implementation.</p>
<p>My solution to this was to use the idea of inner and outer iterators. In this
particular case, I created a <code>PriorityQueue</code> class that composes an
<code>SplPriorityQueue</code> instance, and which also implements <code>IteratorAggregate</code>. This
allows the following:</p>
<pre><code class="language-php hljs php" data-lang="php"><span class="hljs-keyword">namespace</span> <span class="hljs-title">Foo</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PriorityQueue</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Countable</span>, <span class="hljs-title">IteratorAggregate</span>
</span>{
    <span class="hljs-keyword">protected</span> $innerQueue;
    
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__construct</span><span class="hljs-params">()</span>
    </span>{
        <span class="hljs-comment">// I'll explain the lack of global namespacing later...</span>
        <span class="hljs-keyword">$this</span>-&gt;innerQueue = <span class="hljs-keyword">new</span> SplPriorityQueue;
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">count</span><span class="hljs-params">()</span>
    </span>{
        <span class="hljs-keyword">return</span> count(<span class="hljs-keyword">$this</span>-&gt;innerQueue);
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">insert</span><span class="hljs-params">($datum, $priority)</span>
    </span>{
        <span class="hljs-keyword">$this</span>-&gt;innerQueue-&gt;insert($datum, $priority);
    }
    
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getIterator</span><span class="hljs-params">()</span>
    </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">clone</span> <span class="hljs-keyword">$this</span>-&gt;innerQueue;
    }
}
</code></pre>
<p>This approach means that as I consume <code>PriorityQueue</code>, I can be assured that I
can count and iterate over it… again and again.</p>
<p>I mention in the code comments that I'm not importing <code>SplPriorityQueue</code> into
the namespace. The reason is that I want to also solve the problem of
predictable queue order.</p>
<h3>Enforcing predictable queue order</h3>
<p>The solution to the queue order problem with equal priorities is actually quite
simple. While I found it on <a href="http://php.net/splpriorityqueue.compare">the SplPriorityQueue::compare manual page</a>,
<a href="http://twitter.com/elazar">Matthew Turland</a> also
<a href="http://www.slideshare.net/tobias382/new-spl-features-in-php-53">discusses it in a presentation on SPL</a>,
and it hinges on one, simple fact: <em>priorities do not need to be integers</em>.</p>
<p>What does this mean? It means that the following are not equivalent, and will
lead to a more expected sort order:</p>
<pre><code class="language-php hljs php" data-lang="php">$queue-&gt;insert(<span class="hljs-string">'foo'</span>, <span class="hljs-keyword">array</span>(<span class="hljs-number">1000</span>, <span class="hljs-number">1000</span>));
$queue-&gt;insert(<span class="hljs-string">'bar'</span>, <span class="hljs-keyword">array</span>(<span class="hljs-number">1000</span>, <span class="hljs-number">100</span>));
$queue-&gt;insert(<span class="hljs-string">'baz'</span>, <span class="hljs-keyword">array</span>(<span class="hljs-number">1000</span>, <span class="hljs-number">10</span>));
$queue-&gt;insert(<span class="hljs-string">'bat'</span>, <span class="hljs-keyword">array</span>(<span class="hljs-number">1000</span>, <span class="hljs-number">1</span>));

<span class="hljs-keyword">foreach</span> ($queue <span class="hljs-keyword">as</span> $data) <span class="hljs-keyword">echo</span> $data, <span class="hljs-string">" "</span>;
</code></pre>
<p>This results in &quot;foo bar baz bat&quot;!</p>
<p>The trick, then, is automating the solution. I achieved this in a custom
<code>SplPriorityQueue</code> extension:</p>
<pre><code class="language-php hljs php" data-lang="php"><span class="hljs-keyword">namespace</span> <span class="hljs-title">Foo</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SplPriorityQueue</span> <span class="hljs-keyword">extends</span> \<span class="hljs-title">SplPriorityQueue</span>
</span>{
    <span class="hljs-keyword">protected</span> $queueOrder = PHP_INT_MAX;

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">insert</span><span class="hljs-params">($datum, $priority)</span>
    </span>{
        <span class="hljs-keyword">if</span> (is_int($priority)) {
            $priority = <span class="hljs-keyword">array</span>($priority, <span class="hljs-keyword">$this</span>-&gt;queueOrder--);
        }
        <span class="hljs-keyword">parent</span>::insert($datum, $priority);
    }
}
</code></pre>
<p>As each datum is added to the queue, if the priority is an integer, it wraps it
in an array, using <code>$queueOrder</code> as a second value to the array, and
decrementing <code>$queueOrder</code> on completion. The new priority is then used to
insert the value.</p>
<p>Using this extension ensures that order in the priority queue is now
predictable.</p>
<h2>Conclusions</h2>
<p><code>SplPriorityQueue</code> is indeed powerful, and saves me a ton of time programming —
and also likely CPU processes and memory when using larger data sets. While it
may not always meet my use cases, the fact is that, particularly with
namespacing available, I can easily override the class to meet my needs.</p>


<div class="h-entry">
    <img class="u-photo photo" width="50" src="https://avatars0.githubusercontent.com/u/25943?v=3&u=79dd2ea1d4d8855944715d09ee4c86215027fa80&s=140" alt="matthew">
    <a class="u-url u-uid p-name" href="https://mwop.net/blog/253-Taming-SplPriorityQueue.html">Taming SplPriorityQueue</a> was originally
    published <time class="dt-published" datetime="2011-01-17T10:02:00-06:00">17 January 2011</time>
    on <a href="https://mwop.net">https://mwop.net</a> by
    <a rel="author" class="p-author" href="https://mwop.net">Matthew Weier O&#039;Phinney</a>.
</div>
]]></content:encoded>
      <slash:comments>0</slash:comments>
    </item>
    <item>
      <title>Applying FilterIterator to Directory Iteration</title>
      <pubDate>Fri, 20 Aug 2010 15:45:21 -0500</pubDate>
      <link>https://mwop.net/blog/244-Applying-FilterIterator-to-Directory-Iteration.html</link>
      <guid>https://mwop.net/blog/244-Applying-FilterIterator-to-Directory-Iteration.html</guid>
      <author>contact@mwop.net (Matthew Weier O'Phinney)</author>
      <dc:creator>Matthew Weier O'Phinney</dc:creator>
      <content:encoded><![CDATA[<p>I'm currently doing research and prototyping for autoloading alternatives in
<a href="http://framework.zend.com/">Zend Framework</a> 2.0. One approach I'm looking at
involves creating explicit class/file maps; these tend to be much faster than
using the <code>include_path</code>, but do require some additional setup.</p>
<p>My algorithm for generating the maps was absurdly simple:</p>
<ul>
<li>Scan the filesystem for PHP files</li>
<li>If the file does not contain an interface, class, or abstract class, skip it.</li>
<li>If it does, get its declared namespace and classname</li>
</ul>
<p>The question was what implementation approach to use.</p>
<p>I'm well aware of <code>RecursiveDirectoryIterator</code>, and planned to use that.
However, I also had heard of <code>FilterIterator</code>, and wondered if I could tie that
in somehow. In the end, I could, but the solution was non-obvious.</p>


<h2>What I Thought I'd Be Able To Do</h2>
<p><code>FilterIterator</code> is an abstract class. When extending it, you must define an
<code>accept()</code> method.</p>
<pre><code class="language-php hljs php" data-lang="php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">FooFilter</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">FilterIterator</span>
</span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">accept</span><span class="hljs-params">()</span>
    </span>{
    }
}
</code></pre>
<p>In that method, you typically will inspect whatever is returned by
<code>$this-&gt;current()</code>, and then return a boolean <code>true</code> or <code>false</code>, depending on
whether you want to keep it or not.</p>
<pre><code class="language-php hljs php" data-lang="php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">FooFilter</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">FilterIterator</span>
</span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">accept</span><span class="hljs-params">()</span>
    </span>{
        $item = <span class="hljs-keyword">$this</span>-&gt;current();

        <span class="hljs-keyword">if</span> ($someCriteriaIsMet) {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
        }

        <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
    }
}
</code></pre>
<p>I'll go into the mechanics of my criteria later; what's important now is knowing
that a <code>FilterIterator</code> allows you to limit the results returned by your
iterator.</p>
<p>I originally thought I'd be able to simply pass a <code>DirectoryIterator</code> or
<code>RecursiveDirectoryIterator</code> to my filtering instance. This worked in the former
case, as it's only one level deep. However, for the latter, it would only return
the first directory level for all classes that matched — i.e., if I ran it over
<code>Zend/Controller</code>, I'd get a match for each class under
<code>Zend/Controller/Action/Helper/</code>, but it would return simply
<code>Zend/Controller/Action</code> as the match. This certainly wasn't useful.</p>
<p>I then discovered <code>RecursiveFilterIterator</code>, which looked like it would solve
the recursion problem. However, I found one of two results occurred: either I'd
receive an entire subtree if at least one item matched, or it would skip an
entire subtree if the first item found failed the criteria. There was no middle
ground.</p>
<h2>The Solution</h2>
<p>The solution was incredibly simple and elegant, once I stumbled upon it: pass my
<code>RecursiveIteratorIterator</code> instance to the <code>FilterIterator</code>.</p>
<pre><code class="language-php hljs php" data-lang="php">$rdi      = <span class="hljs-keyword">new</span> RecursiveDirectoryIterator($somePath);
$rii      = <span class="hljs-keyword">new</span> RecursiveIteratorIterator($rdi);
$filtered = <span class="hljs-keyword">new</span> FooFilter($rii);
</code></pre>
<p>Really. It was that simple — but, as noted, non-obvious. It also required a
slight change within my filter — instead of using <code>current()</code>, I'd need to first
pull the &quot;inner&quot; iterator instance: <code>$this-&gt;getInnerIterator()-&gt;current()</code>. I
show an example of that below when I go over the filter implementation.</p>
<p>As for my criteria, I had several options. I could <code>require_once</code> the file, and
use the Reflection API to inspect the class to determine if it was an interface,
abstract class, or class, as well as to determine the namespace. However, I
couldn't be 100% sure the file would contain a class, so this seemed like
overkill. That, and horribly non-performant, due to using reflection.</p>
<p>The next option was to simply slurp in the file contents into a variable, and
use regular expressions. I love regular expressions, but in this case, it felt
like I could possibly end up with some false positives. Also, since some of
these files could be quite large, I was worried again about performance
implications — I don't want to have to wait forever to generate these maps.</p>
<p>The solution I went with was to use the <a href="http://php.net/tokenizer">tokenizer</a> to
inspect the file. Tokenizing is incredibly fast, and it's also incredibly simple
to analyze the tokens.</p>
<p>I decided to store the detected namespace and classnames as public properties of
the <code>SplFileInfo</code> objects returned; this makes it simple to iterate over the
entire collection and utilize that information. Also, because I have
<code>SplFileInfo</code> objects, I already have the paths I need.</p>
<p>My implementation looks like this:</p>
<pre><code class="language-php hljs php" data-lang="php"><span class="hljs-comment">/** <span class="hljs-doctag">@namespace</span> */</span>
<span class="hljs-keyword">namespace</span> <span class="hljs-title">Zend</span>\<span class="hljs-title">File</span>;

<span class="hljs-comment">// import SPL classes/interfaces into local scope</span>
<span class="hljs-keyword">use</span> <span class="hljs-title">DirectoryIterator</span>,
    <span class="hljs-title">FilterIterator</span>,
    <span class="hljs-title">RecursiveIterator</span>,
    <span class="hljs-title">RecursiveDirectoryIterator</span>,
    <span class="hljs-title">RecursiveIteratorIterator</span>;

<span class="hljs-comment">/**
 * Locate files containing PHP classes, interfaces, or abstracts
 * 
 * <span class="hljs-doctag">@package</span>    Zend_File
 * <span class="hljs-doctag">@license</span>    New BSD {<span class="hljs-doctag">@link</span> http://framework.zend.com/license/new-bsd}
 */</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ClassFileLocater</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">FilterIterator</span>
</span>{
    <span class="hljs-comment">/**
     * Create an instance of the locater iterator
     * 
     * Expects either a directory, or a DirectoryIterator (or its recursive variant) 
     * instance.
     * 
     * <span class="hljs-doctag">@param</span>  string|DirectoryIterator $dirOrIterator 
     * <span class="hljs-doctag">@return</span> void
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__construct</span><span class="hljs-params">($dirOrIterator = <span class="hljs-string">'.'</span>)</span>
    </span>{
        <span class="hljs-keyword">if</span> (is_string($dirOrIterator)) {
            <span class="hljs-keyword">if</span> (!is_dir($dirOrIterator)) {
                <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> InvalidArgumentException(<span class="hljs-string">'Expected a valid directory name'</span>);
            }

            $dirOrIterator = <span class="hljs-keyword">new</span> RecursiveDirectoryIterator($dirOrIterator);
        }
        <span class="hljs-keyword">if</span> (!$dirOrIterator <span class="hljs-keyword">instanceof</span> DirectoryIterator) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> InvalidArgumentException(<span class="hljs-string">'Expected a DirectoryIterator'</span>);
        }

        <span class="hljs-keyword">if</span> ($dirOrIterator <span class="hljs-keyword">instanceof</span> RecursiveIterator) {
            $iterator = <span class="hljs-keyword">new</span> RecursiveIteratorIterator($dirOrIterator);
        } <span class="hljs-keyword">else</span> {
            $iterator = $dirOrIterator;
        }

        <span class="hljs-keyword">parent</span>::__construct($iterator);
        <span class="hljs-keyword">$this</span>-&gt;rewind();
    }

    <span class="hljs-comment">/**
     * Filter for files containing PHP classes, interfaces, or abstracts
     * 
     * <span class="hljs-doctag">@return</span> bool
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">accept</span><span class="hljs-params">()</span>
    </span>{
        $file = <span class="hljs-keyword">$this</span>-&gt;getInnerIterator()-&gt;current();

        <span class="hljs-comment">// If we somehow have something other than an SplFileInfo object, just </span>
        <span class="hljs-comment">// return false</span>
        <span class="hljs-keyword">if</span> (!$file <span class="hljs-keyword">instanceof</span> \SplFileInfo) {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
        }

        <span class="hljs-comment">// If we have a directory, it's not a file, so return false</span>
        <span class="hljs-keyword">if</span> (!$file-&gt;isFile()) {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
        }

        <span class="hljs-comment">// If not a PHP file, skip</span>
        <span class="hljs-keyword">if</span> ($file-&gt;getBasename(<span class="hljs-string">'.php'</span>) == $file-&gt;getBasename()) {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
        }

        $contents = file_get_contents($file-&gt;getRealPath());
        $tokens   = token_get_all($contents);
        $count    = count($tokens);
        $i        = <span class="hljs-number">0</span>;
        <span class="hljs-keyword">while</span> ($i &lt; $count) {
            $token = $tokens[$i];

            <span class="hljs-keyword">if</span> (!is_array($token)) {
                <span class="hljs-comment">// single character token found; skip</span>
                $i++;
                <span class="hljs-keyword">continue</span>;
            }

            <span class="hljs-keyword">list</span>($id, $content, $line) = $token;

            <span class="hljs-keyword">switch</span> ($id) {
                <span class="hljs-keyword">case</span> T_NAMESPACE:
                    <span class="hljs-comment">// Namespace found; grab it for later</span>
                    $namespace = <span class="hljs-string">''</span>;
                    $done      = <span class="hljs-keyword">false</span>;
                    <span class="hljs-keyword">do</span> {
                        ++$i;
                        $token = $tokens[$i];
                        <span class="hljs-keyword">if</span> (is_string($token)) {
                            <span class="hljs-keyword">if</span> (<span class="hljs-string">';'</span> === $token) {
                                $done = <span class="hljs-keyword">true</span>;
                            }
                            <span class="hljs-keyword">continue</span>;
                        }
                        <span class="hljs-keyword">list</span>($type, $content, $line) = $token;
                        <span class="hljs-keyword">switch</span> ($type) {
                            <span class="hljs-keyword">case</span> T_STRING:
                            <span class="hljs-keyword">case</span> T_NS_SEPARATOR:
                                $namespace .= $content;
                                <span class="hljs-keyword">break</span>;
                        }
                    } <span class="hljs-keyword">while</span> (!$done &amp;&amp; $i &lt; $count);

                    <span class="hljs-comment">// Set the namespace of this file in the object</span>
                    $file-&gt;namespace = $namespace;
                    <span class="hljs-keyword">break</span>;
                <span class="hljs-keyword">case</span> T_ABSTRACT:
                <span class="hljs-keyword">case</span> T_CLASS:
                <span class="hljs-keyword">case</span> T_INTERFACE:
                    <span class="hljs-comment">// Abstract class, class, or interface found</span>

                    <span class="hljs-comment">// Get the classname</span>
                    $class = <span class="hljs-string">''</span>;
                    <span class="hljs-keyword">do</span> {
                        ++$i;
                        $token = $tokens[$i];
                        <span class="hljs-keyword">if</span> (is_string($token)) {
                            <span class="hljs-keyword">continue</span>;
                        }
                        <span class="hljs-keyword">list</span>($type, $content, $line) = $token;
                        <span class="hljs-keyword">switch</span> ($type) {
                            <span class="hljs-keyword">case</span> T_STRING:
                                $class = $content;
                                <span class="hljs-keyword">break</span>;
                        }
                    } <span class="hljs-keyword">while</span> (<span class="hljs-keyword">empty</span>($class) &amp;&amp; $i &lt; $count);

                    <span class="hljs-comment">// If a classname was found, set it in the object, and </span>
                    <span class="hljs-comment">// return boolean true (found)</span>
                    <span class="hljs-keyword">if</span> (!<span class="hljs-keyword">empty</span>($class)) {
                        $file-&gt;classname = $class;
                        <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
                    }
                    <span class="hljs-keyword">break</span>;
                <span class="hljs-keyword">default</span>:
                    <span class="hljs-keyword">break</span>;
            }
            ++$i;
        }

        <span class="hljs-comment">// No class-type tokens found; return false</span>
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
    }
}
</code></pre>
<p><em>Note: the Exceptions thrown in this class are defined in the same namespace;
I'll leave how they're implemented to your imagination.</em></p>
<h2>Iterating Faster</h2>
<p>The next trick I discovered was in the form of <code>iterator_apply()</code>. Normally when
I use iterators, I use <code>foreach</code>, because, well, that's what you do. But in
looking through the various iterators for this exercise, I stumbled across this
gem.</p>
<p>Basically, you pass the iterator, a callback, and argument(s) you want passed to
the callback. Like <code>FilterIterator</code>, you don't get the actual item returned by
the iterator, so in most use cases, you pass the iterator itself:</p>
<pre><code class="language-php hljs php" data-lang="php">iterator_apply($it, $callback, <span class="hljs-keyword">array</span>($it));
</code></pre>
<p>You can then grab the current value and/or key from the iterator itself:</p>
<pre><code class="language-php hljs php" data-lang="php"><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">process</span><span class="hljs-params">(Iterator $it)</span>
</span>{
    $value = $it-&gt;current();
    $key   = $it-&gt;key();
    <span class="hljs-comment">// ...</span>
}
</code></pre>
<p>While you can use any valid PHP callback, I found the most interesting solution
was to use a closure, as it allows you to define everything up front:</p>
<pre><code class="language-php hljs php" data-lang="php">iterator_apply($it, <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">()</span> <span class="hljs-title">use</span> <span class="hljs-params">($it)</span> </span>{
    $value = $it-&gt;current();
    $key   = $it-&gt;key();
    <span class="hljs-comment">// ...</span>
});
</code></pre>
<p>If you pass in a local value via a <code>use</code> statement, you can do some aggregation:</p>
<pre><code class="language-php hljs php" data-lang="php">$map = <span class="hljs-keyword">new</span> \stdClass;
iterator_apply($it, <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">()</span> <span class="hljs-title">use</span> <span class="hljs-params">($it, $map)</span> </span>{
    $file = $it-&gt;current();
    $namespace = !<span class="hljs-keyword">empty</span>($file-&gt;namespace) ? $file-&gt;namespace . <span class="hljs-string">'\' : '</span><span class="hljs-string">';
    $classname = $namespace . $file-&gt;classname;
    $map-&gt;{$classname} = $file-&gt;getPathname();
});
</span></code></pre>
<p>Not only is this a nice, concise technique, it's also tremendously fast — I was
finding it was 200%–300% faster than using a traditional <code>foreach</code> loop.
Clearly it cannot be used in all situations, but if you <em>can</em> use it, you
probably should.</p>
<p>So, start playing with <code>FilterIterator</code> and <code>iterator_apply()</code> if you haven't
already — the two offer tremendous possibilities and capabilities for your applications.</p>


<div class="h-entry">
    <img class="u-photo photo" width="50" src="https://avatars0.githubusercontent.com/u/25943?v=3&u=79dd2ea1d4d8855944715d09ee4c86215027fa80&s=140" alt="matthew">
    <a class="u-url u-uid p-name" href="https://mwop.net/blog/244-Applying-FilterIterator-to-Directory-Iteration.html">Applying FilterIterator to Directory Iteration</a> was originally
    published <time class="dt-published" datetime="2010-08-16T10:30:00-05:00">16 August 2010</time>
    on <a href="https://mwop.net">https://mwop.net</a> by
    <a rel="author" class="p-author" href="https://mwop.net">Matthew Weier O&#039;Phinney</a>.
</div>
]]></content:encoded>
      <slash:comments>0</slash:comments>
    </item>
  </channel>
</rss>
