<?xml version="1.0" encoding="utf-8" ?>
<?xml-stylesheet href="/it/templates/default/atom.css" type="text/css" ?>

<feed 
   xmlns="http://www.w3.org/2005/Atom"
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
   xmlns:dc="http://purl.org/dc/elements/1.1/"
   xmlns:admin="http://webns.net/mvcb/"
   xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
   xmlns:wfw="http://wellformedweb.org/CommentAPI/">
    
    <link href="http://www.zeilenwechsel.de/it/feeds/atom10.xml" rel="self" title="Zeilenwechsel.de" type="application/atom+xml" />
    <link href="http://www.zeilenwechsel.de/it/"                        rel="alternate"    title="Zeilenwechsel.de" type="text/html" />
    <link href="http://www.zeilenwechsel.de/it/rss.php?version=2.0"     rel="alternate"    title="Zeilenwechsel.de" type="application/rss+xml" />
    <title type="html">Zeilenwechsel.de</title>
    <subtitle type="html">IT Stuff</subtitle>
    <icon>http://www.zeilenwechsel.de/it/templates/zeilenwechsel/img/s9y_banner_small.png</icon>
    <id>http://www.zeilenwechsel.de/it/</id>
    <updated>2011-07-27T20:47:56Z</updated>
    <generator uri="http://www.s9y.org/" version="1.5.5">Serendipity 1.5.5 - http://www.s9y.org/</generator>
    <dc:language>en</dc:language>

    <entry>
        <link href="http://www.zeilenwechsel.de/it/articles/6/.Promises-Handling-collections-of-jQuery-Deferreds.html" rel="alternate" title="$.Promises: Handling collections of jQuery Deferreds" />
        <author>
            <name>_ michael</name>
                    </author>
    
        <published>2011-07-27T16:11:34Z</published>
        <updated>2011-07-27T20:47:56Z</updated>
        <wfw:comment>http://www.zeilenwechsel.de/it/wfwcomment.php?cid=6</wfw:comment>
    
        <slash:comments>0</slash:comments>
        <wfw:commentRss>http://www.zeilenwechsel.de/it/rss.php?version=atom1.0&amp;type=comments&amp;cid=6</wfw:commentRss>
    
            <category scheme="http://www.zeilenwechsel.de/it/categories/jQuery" label="jQuery" term="jQuery" />
    
        <id>http://www.zeilenwechsel.de/it/articles/6/guid.html</id>
        <title type="html">$.Promises: Handling collections of jQuery Deferreds</title>
        <content type="xhtml" xml:base="http://www.zeilenwechsel.de/it/">
            <div xmlns="http://www.w3.org/1999/xhtml">
                <p><a href="http://api.jquery.com/category/deferred-object">jQuery Deferreds</a> are a convenient way to manange asynchronous responses in Javascript. Recently, I came up against <a href="http://www.zeilenwechsel.de/it/code/browse/qunit-cacheable-resources/testmanager.js">a problem</a> where I had to handle groups of Deferreds (or rather, <a href="http://api.jquery.com/Types/#Promise">promises</a>) collectively, and add more promises to those groups later in the process. To improve the readability and flexibility of the code, I wrapped the Deferreds of each group in a new kind of $.Promises object, which I'd like to introduce here.</p>

<h2>What are $.Promises?</h2>

<p>The <code>$.Promises</code> object is a convenience wrapper around arrays of jQuery Deferreds or promises. It helps to collect Deferreds and add new ones later on, to delay their resolution and pass them to <a href="http://api.jquery.com/jQuery.when/"><code>$.when</code></a> even before all Deferreds of the collection are set up.</p>

<p>In short, a <code>$.Promises</code> collection adds another layer of asynchronicity to Deferreds and provides an easy-to-read API at the same time.</p>

<h2>Making Promises is easy - postponing them, too</h2>

<p>Here's how Promises work:</p>

<pre><code><span class="geshiContainer"><span style="color: #003366; font-weight: bold;">var</span> myPromises <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">new</span> $.<span style="color: #660066;">Promises</span><span style="color: #009900;">(</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span></span>
</code></pre>

<p>'<code>new</code>' is optional. We don't have to add Deferreds to the collection at this point, but we can:</p>

<pre><code><span class="geshiContainer"><span style="color: #003366; font-weight: bold;">var</span> myPromises <span style="color: #339933;">=</span> $.<span style="color: #660066;">Promises</span><span style="color: #009900;">(</span> dfd1<span style="color: #339933;">,</span> dfd2 <span style="color: #009900;">)</span><span style="color: #339933;">;</span></span>
</code></pre>

<p>This creates an aggregate promise which will resolve or fail according to the Deferreds 'inside' of it.</p>

<pre><code><span class="geshiContainer">myPromises.<span style="color: #660066;">done</span><span style="color: #009900;">(</span> ... <span style="color: #009900;">)</span>.<span style="color: #660066;">fail</span><span style="color: #009900;">(</span> ... <span style="color: #009900;">)</span><span style="color: #339933;">;</span>
$.<span style="color: #660066;">when</span><span style="color: #009900;">(</span> myPromises <span style="color: #009900;">)</span>.<span style="color: #660066;">done</span><span style="color: #009900;">(</span> ... <span style="color: #009900;">)</span><span style="color: #339933;">;</span></span>
</code></pre>

<p>We can add more promises or Deferreds to the collection even if the current ones have all resolved. We just need to treat these Promises like new-year resolutions ;-)</p>

<pre><code><span class="geshiContainer"><span style="color: #006600; font-style: italic;">// We delay the resolution of our promises and add some more</span>
myPromises.<span style="color: #660066;">postpone</span><span style="color: #009900;">(</span><span style="color: #009900;">)</span>
          .<span style="color: #660066;">add</span><span style="color: #009900;">(</span> dfd3<span style="color: #339933;">,</span> dfd4 <span style="color: #009900;">)</span><span style="color: #339933;">;</span>
 
<span style="color: #006600; font-style: italic;">// Now we attach a done handler</span>
$.<span style="color: #660066;">when</span><span style="color: #009900;">(</span> myPromises <span style="color: #009900;">)</span>.<span style="color: #660066;">done</span><span style="color: #009900;">(</span> whatever <span style="color: #009900;">)</span><span style="color: #339933;">;</span>
 
<span style="color: #006600; font-style: italic;">// ... and resolve all promises.</span>
dfd1.<span style="color: #660066;">resolve</span><span style="color: #009900;">(</span> somearg <span style="color: #009900;">)</span><span style="color: #339933;">;</span> dfd2.<span style="color: #660066;">resolve</span><span style="color: #009900;">(</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span></span><span class="geshiContainer"> ... etc for all Deferreds
</span></code></pre>

<p>That would resolve the collection if we had not postponed it. But we have, so we can still add more stuff, and <code>$.when</code> will wait for us to finish:</p>

<pre><code><span class="geshiContainer"><span style="color: #006600; font-style: italic;">// $.when( myPromises ), called earlier, will respond to these additions</span>
myPromises.<span style="color: #660066;">add</span><span style="color: #009900;">(</span> dfd5 <span style="color: #009900;">)</span><span style="color: #339933;">;</span></span><span class="geshiContainer"> 
...
</span></code></pre>

<p>When all is set up, <code>$.when</code> will act on the updated collection. This can't be done with ordinary arrays of promises. Finally,</p>

<pre><code><span class="geshiContainer">myPromises.<span style="color: #660066;">stopPostponing</span><span style="color: #009900;">(</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span></span>
</code></pre>

<p>will unblock the resolution or rejection of myPromises.</p>

<h2>The API</h2>

<ul>
<li><p><code>$.Promises( [deferred, [deferred]] )</code></p>

<p>Constructor, returns a new Promises object. A list of promises can be passed as arguments (optional). Can be called with or without '<code>new</code>'.</p></li>
<li><p><code>.add( promise, [promise] )</code></p>

<p>Adds one or more promises to the collection. Also accepts Deferreds. Returns the Promises object.</p></li>
<li><p><code>.postpone()</code></p>

<p>Blocks the resolution of the aggregate Promises. Returns the Promises object.</p>

<p>Calling <code>.postpone()</code> is useful</p>

<ul>
<li>if you pass Promises to <code>$.when</code> while you are still adding new promises to the collection, and want them to impact <code>$.when()</code>.</li>
<li>if you attach <code>.done()</code> and <code>.fail()</code> handlers early, before you have made all your promises.</li>
<li>if you are in the process of gathering promises while the ones you have already added might resolve at any time. If all of them do, the collection resolves as well - unless you have called <code>postpone()</code> to keep the collection open for more promises.</li>
</ul></li>
<li><p><code>.stopPostponing()</code></p>

<p>Unblocks the resolution of the collected promises if it has been delayed by <code>postpone()</code>. Returns the Promises object.</p></li>
<li><p><code>.ignoreBelated( [yesno] )</code></p>

<p>Makes the Promise object ignore attempts to add promises, or call <code>postpone()</code>, when it is too late. Normally, these actions throw a <code>PromisesError</code> exception if they happen after the eventual resolution or failure of the Promise.</p>

<p>Can be turned off again by calling <code>.ignoreBelated( false )</code>. Returns the Promises object.</p></li>
<li><p><code>.isUnresolved()</code></p>

<p>Returns if the Promises object is still unresolved.</p></li>
</ul>

<h2>Dependencies</h2>

<p>The <code>$.Promises</code> extension requires jQuery 1.6.0 or newer.</p>

<h2>A caveat</h2>

<p>The <code>$.Promises</code> object is not built with performance in mind. <a href="http://www.zeilenwechsel.de/it/code/browse/jquery-promises/prod/promises.js">Look at the code</a> - you won't see any of the 'low-level' stuff which makes up the jQuery implementation of Deferreds. Rather, <code>$.Promises</code> is built on top of Deferreds. As a result, the code easy to read and maintain, but the implementation is not as efficient as it could be. That's the tradeoff.</p>

<p>You might find it reassuring to know, though, that the <code>$.Promises</code> extension is <a href="http://www.zeilenwechsel.de/it/code/get/jquery-promises/tests/promises.html">fully tested</a>.</p>

<p><a href="http://www.zeilenwechsel.de/it/code/browse/jquery-promises/prod/promises.js">View the source</a> | <a href="http://www.zeilenwechsel.de/it/code/get/jquery-promises/tests/promises.html">Run the tests</a> | <a href="http://www.zeilenwechsel.de/it/code/download/jquery-promises/prod/promises.js">Dowload the extension</a></p>
 
            </div>
        </content>
        <dc:subject>Javascript</dc:subject>
<dc:subject>jQuery</dc:subject>

    </entry>
    <entry>
        <link href="http://www.zeilenwechsel.de/it/articles/5/How-max-width-fails-in-IE8.html" rel="alternate" title="How max-width fails in IE8" />
        <author>
            <name>_ michael</name>
                    </author>
    
        <published>2011-06-07T13:08:00Z</published>
        <updated>2011-07-26T22:21:18Z</updated>
        <wfw:comment>http://www.zeilenwechsel.de/it/wfwcomment.php?cid=5</wfw:comment>
    
        <slash:comments>0</slash:comments>
        <wfw:commentRss>http://www.zeilenwechsel.de/it/rss.php?version=atom1.0&amp;type=comments&amp;cid=5</wfw:commentRss>
    
            <category scheme="http://www.zeilenwechsel.de/it/categories/Browser-Bugs" label="Browser Bugs" term="Browser Bugs" />
    
        <id>http://www.zeilenwechsel.de/it/articles/5/guid.html</id>
        <title type="html">How max-width fails in IE8</title>
        <content type="xhtml" xml:base="http://www.zeilenwechsel.de/it/">
            <div xmlns="http://www.w3.org/1999/xhtml">
                <p>Flexible, fluid, liquid, <a href="http://www.alistapart.com/articles/responsive-web-design/">responsive</a> - layouts which don't rely on a fixed width have had a comeback recently. The CSS <code>max-width</code> property is <a href="http://adactio.com/journal/4538/">very</a> <a href="http://www.alistapart.com/articles/fluid-images/">useful</a> in these designs. So is the knowledge about related browser bugs. Enter IE8.</p>

<h2>Images and max-width</h2>

<p>When <code>max-width</code> is applied to an <code>&lt;img&gt;</code> tag, the effect should not be restricted to the width of the image. The height has to be adjusted as well, unless there is another rule explicitly setting a value for it.</p>

<p>For instance, a stylesheet might define a <code>width: 100%</code> to make an image fill its container, but limit its size to <code>max-with: 200px</code>. The browser will use both rules to calculate the effective width and, absent any directives for the height, rescale the image: The height is set in proportion to the width. This preserves the aspect ratio of the image and avoids ugly distortions.</p>

<p>This behaviour actually applies to any <a href="http://reference.sitepoint.com/css/replacedelements">replaced element</a> that has an intrinsic ratio of width and height. In practice, though, this means images.</p>

<h2>What IE8 makes of it</h2>

<p>Yet unfortunately IE8 doesn't play by the rules. When both <code>width</code> and <code>max-width</code> are specified and the max-width limit is the lower of the two, the limit should be binding. Indeed, IE8 applies <code>max-width</code> to the width of the image, just as it should.</p>

<p>But the image height remains as if the max-width limit had not come into effect: IE8 calculates the height in proportion to the overridden <code>width</code> rule. The affected images are compressed horizontally (or stretched vertically, depending on how you look at it). This bug is an IE8 peculiarity - it didn't exist in IE7, and has vanished again in IE9.</p>

<p>This is what it looks like:</p>

<div style="text-align: center;">

<div class="serendipity_imageComment_center" style="width: 216px; padding-right: 10px"><div class="serendipity_imageComment_txt">In IE8</div><div class="serendipity_imageComment_img"><!-- s9ymdb:4 --><img class="serendipity_image_center" width="216" height="540" src="http://www.zeilenwechsel.de/it/uploads/pics/2011/05/max-width-ie8.png" title="max-width image distortion in IE8" alt="max-width image distortion in IE8" /></div></div>

<div class="serendipity_imageComment_center" style="width: 215px; padding-left: 10px"><div class="serendipity_imageComment_txt">In standards-compliant browsers</div><div class="serendipity_imageComment_img"><!-- s9ymdb:5 --><img class="serendipity_image_center" width="215" height="540" src="http://www.zeilenwechsel.de/it/uploads/pics/2011/05/max-width-chrome.png" title="max-width in standards-compliant browsers" alt="max-width in standards-compliant browsers" /></div></div>

</div>

<p>If you'd like to see it on a live page, have a look at the <a href="http://www.zeilenwechsel.de/it/samples/ie8-max-width-bug/buggy.html">demo</a>. (Of course, you'll only be seeing the distortion if you are viewing the page in IE8.)</p>

<h2>How to fix it</h2>

<p>There are two ways to deal with this problem, both of them straightforward.</p>

<h3>Solution #1: Fix the CSS</h3>

<p>The key to making IE8 behave is setting a <code>max-height</code> in proportion to <code>max-width</code>. Suppose an image is 200px wide and 100px tall - and here we are taking about the dimensions of the actual image file, not the display size imposed on it with CSS. Now if <code>max-width</code> limits the image to 50px, <code>max-height</code> should be set to 25px. <a href="http://www.zeilenwechsel.de/it/samples/ie8-max-width-bug/fixed-with-max-height.html">Done</a>.</p>

<p>The catch is that you have to know the image size in advance. Obviously, this is not an issue with static pages. But if your content is coming out of a CMS, it might be impossible to hard-code these values into the CSS.</p>

<h3>Solution #2: Use a script for dynamic pages</h3>

<p>Instead of writing fixed <code>max-width</code> values into a CSS file, they can be generated on the fly. This is exactly what the <a href="http://www.zeilenwechsel.de/it/code/browse/ie8-max-width-fix/prod/ie8_fix_maxwidth.js?root=prod">jQuery script</a> below <a href="http://www.zeilenwechsel.de/it/samples/ie8-max-width-bug/fixed-with-js.html">does</a>.</p>

<p>Usage is simple. Put a conditional comment for IE8 in your page source. Within it, load a copy of the <a href="http://docs.jquery.com/Downloading_jQuery">jQuery library</a> if it isn't available already, then <a href="http://www.zeilenwechsel.de/it/code/download/ie8-max-width-fix/prod/ie8_fix_maxwidth.js">load the script</a>. That's all there is to it.</p>

<pre><code><span class="geshiContainer">&lt;!--[if IE 8]&gt;
     &lt;script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js" 
        type="text/javascript"&gt;&lt;/script&gt;
     &lt;script src="[YOUR PATH HERE]/ie8_fix_maxwidth.js" 
        type="text/javascript"&gt;&lt;/script&gt;
&lt;![endif]--&gt;</span>
</code></pre>

<p>This solution may also be useful for static pages. Including the script makes the IE8 bug a non-issue, so it might make sense to use it on every relevant page and just forget about the problem. Again, the script should be wrapped in a conditional comment, as above.</p>

<p>Potential issues, including those caused by IE's caching and by slow connections, should be covered just fine. For more details about how the script works, just have a look at the <a href="http://www.zeilenwechsel.de/it/code/browse/ie8-max-width-fix/prod/ie8_fix_maxwidth.js?root=prod">commented source</a>.</p>

<p><a href="http://www.zeilenwechsel.de/it/code/browse/ie8-max-width-fix/prod/ie8_fix_maxwidth.js?root=prod">View source (fully commented)</a> | <a href="http://www.zeilenwechsel.de/it/code/download/ie8-max-width-fix/prod/ie8_fix_maxwidth.js">Download</a></p>
 
            </div>
        </content>
        <dc:subject>CSS</dc:subject>
<dc:subject>Internet Explorer</dc:subject>

    </entry>
    <entry>
        <link href="http://www.zeilenwechsel.de/it/articles/4/A-PHP-test-harness-for-Komodo.html" rel="alternate" title="A PHP test harness for Komodo" />
        <author>
            <name>_ michael</name>
                    </author>
    
        <published>2011-06-02T12:45:00Z</published>
        <updated>2012-04-17T15:27:29Z</updated>
        <wfw:comment>http://www.zeilenwechsel.de/it/wfwcomment.php?cid=4</wfw:comment>
    
        <slash:comments>0</slash:comments>
        <wfw:commentRss>http://www.zeilenwechsel.de/it/rss.php?version=atom1.0&amp;type=comments&amp;cid=4</wfw:commentRss>
    
            <category scheme="http://www.zeilenwechsel.de/it/categories/PHPUnit" label="PHPUnit" term="PHPUnit" />
    
        <id>http://www.zeilenwechsel.de/it/articles/4/guid.html</id>
        <title type="html">A PHP test harness for Komodo</title>
        <content type="xhtml" xml:base="http://www.zeilenwechsel.de/it/">
            <div xmlns="http://www.w3.org/1999/xhtml">
                <p>I've been using <a href="http://www.activestate.com/komodo-ide">Komodo</a> a long time already for PHP and web development. It has a feature to <a href="http://docs.activestate.com/komodo/6.1/unittest.html">run unit tests</a> inside the IDE, but frankly it was never really working well, and I've been using my own setup. With <a href="https://github.com/sebastianbergmann/phpunit/">PHPUnit</a> 3.5, the Komodo test feature is finally broken. So now is probably a good time to share a replacement.</p>

<p><em>Update 17 April 2012:</em> The test harness now supports XML configuration files. It is compatible with Komodo IDE 6 and 7 (maybe also with older versions) and is tested with PHPUnit 3.4 through 3.6.</p>

<h2>Why use it? What's the deal?</h2>

<p>The new harness fixes a whole bunch of issues:</p>

<ul>
<li>It works with current versions of PHPUnit. Without it, Komodo just quits with an error message.</li>
<li>It supports advanced PHPUnit features like <a href="http://www.phpunit.de/manual/3.5/en/writing-tests-for-phpunit.html#writing-tests-for-phpunit.data-providers">@dataProvider</a>. By contrast, the Komodo harness fails in the worst possible way when encoutering this construct, namely by resetting the count of failed tests to zero (!) and continuing as if nothing has happened.</li>
<li>The new harness reports fatal errors during test execution in a way that will get your attention. Previously, Komodo just added an info entry at the very end of the list of tests, without signalling an error or alerting you to the premature exit of the test suite.</li>
<li>The new harness is <em>way</em> faster than the built-in version, provided you suppress the enumeration of all "green" tests (which are uninteresting anyway and perhaps even distracting). You can actually run large test suites without the extended coffee break you got used to ;-)</li>
<li>The output generated by the new harness is as detailed as that provided by PHPUnit in a terminal, but more structured and <em>much</em> easier to navigate if a lot of tests fail.</li>
<li>It will present you with a more readable list of tests than either Komodo or PHPUnit, by transforming those VeryLongConcatenated_AndUnderscored_WormlikeTestNameConstructs into statements formatted for actual humans. (You can turn that feature off if it doesn't match your naming conventions.)</li>
<li>It takes arguments using the <a href="http://www.phpunit.de/manual/3.5/en/textui.html">same syntax as PHPUnit</a>.</li>
<li>It gives you a host of options for customizing the output.</li>
</ul>

<h2>Setup</h2>

<p>The new test harness is a patch for Komodo, not an extension, ie it will replace some Komodo code. Installing it is easy. <a href="http://www.zeilenwechsel.de/it/code/packed/komodo-phpunit-harness/koTestHarness.zip">Download it</a> and drop the extracted files into</p>

<blockquote>
  <p>{Komodo program dir}/lib/mozilla/python/komodo/harnesses/php/</p>
</blockquote>

<p>on Linux and Windows and into</p>

<blockquote>
  <p>/Applications/Komodo\IDE.app/Contents/MacOS/python/komodo/harnesses/php</p>
</blockquote>

<p>on Mac OSX. Overwrite files when prompted. And that's it.</p>

<p>Using it is equally easy. Just create <a href="http://docs.activestate.com/komodo/6.0/unittest.html">test plans</a> as you did before.</p>

<p>By default, the improved version is only active if PHPUnit &gt;= 3.5 is installed. In all other cases, the old Komodo test harness is run from a 'legacy' directory, so installing the patch won't break anyone's existing test plans. You <em>can</em> use the new test harness with PHPUnit 3.4, though, and considering some of the flaws of the built-in functionality, that might actually be a very good idea. Just add <code>--force-new-harness</code> to the arguments of your test plan if your PHPUnit version is older than 3.5.</p>

<h2>Customizing the output</h2>

<p>The new harness will pass your command line arguments on to PHPUnit, which means that you can now use options in <a href="http://www.phpunit.de/manual/3.5/en/textui.html">standard PHPUnit syntax</a>. In addition, the harness supports these switches:</p>

<pre><code><span class="geshiContainer">--in-groups       Display test results grouped by status, problems first. Default.
--in-sequence     Display test results in order of execution.
 
--as-text         Convert test names to descriptive text. Default.
--as-name         Display test names as in the source.
 
--show-class      Show the name of the testcase class along with the method name. Default.
--hide-class      Show the name of the test method only.
 
--show-passed     Display the names of all tests. Default.
--hide-passed     Display the names of failing tests only (including skipped and incomplete
                  tests). [SHOULD be the default because tests run faster by an order of 
                  magnitude, but a Komodo bug needs to be fixed first - see below.]
 
--show-summary    Show the test summary generated by PHPUnit. Default.
--no-summary      Don't show the PHPUnit test summary.
                  [Komodo currently doesn't report skipped and incomplete tests in its own
                  summary, so they might go unnoticed with this option.]
</span></code></pre>

<p>You can change the defaults themselves in <a href="http://www.zeilenwechsel.de/it/code/browse/komodo-phpunit-harness/prod/Ko/PrinterOptions.php?root=prod">/Ko/PrinterOptions.php</a>.</p>

<h2>Caveats?</h2>

<p>Some bugs remain unfixed and can only be handled by the Komodo team. One is worth mentioning. If you use the <code>--hide-passed</code> switch to restrict reporting to failing tests only (and dramatically speed up test execution at the same time), a Komodo bug makes the status bar lose count of passed tests. It will always display "0 passed". That's no big deal, though, because you can see the true number of tests, along with a full test summary, in an information entry on top of the test list.</p>

<p>You'll find more information on the state of the Komodo bugs in the <a href="http://community.activestate.com/node/6742">Komodo support forum</a>.</p>

<h2>Download</h2>

<p>Here it is:</p>

<p><a href="http://www.zeilenwechsel.de/it/code/packed/komodo-phpunit-harness/koTestHarness.zip">Download the files</a> | <a href="http://www.zeilenwechsel.de/it/code/browse/komodo-phpunit-harness/prod/drive_testrunner.php?root=prod">View the source</a></p>

<p>If, and only if, you want to modify the harness itself for your own purposes, use these links which include unit and integration tests for the harness:</p>

<p><a href="http://www.zeilenwechsel.de/it/code/packed/komodo-phpunit-harness/koTestHarnessWithTests.zip">Download the files, with tests</a> | <a href="http://www.zeilenwechsel.de/it/code/browse/komodo-phpunit-harness/prod/drive_testrunner.php">View the source, with tests</a></p>
 
            </div>
        </content>
        <dc:subject>PHP</dc:subject>
<dc:subject>PHPUnit</dc:subject>
<dc:subject>Testing</dc:subject>

    </entry>
    <entry>
        <link href="http://www.zeilenwechsel.de/it/articles/2/jQuery-gets-the-document-size-wrong-in-IE.html" rel="alternate" title="jQuery gets the document size wrong in IE" />
        <author>
            <name>_ michael</name>
                    </author>
    
        <published>2011-05-18T10:12:00Z</published>
        <updated>2011-06-29T20:34:36Z</updated>
        <wfw:comment>http://www.zeilenwechsel.de/it/wfwcomment.php?cid=2</wfw:comment>
    
        <slash:comments>10</slash:comments>
        <wfw:commentRss>http://www.zeilenwechsel.de/it/rss.php?version=atom1.0&amp;type=comments&amp;cid=2</wfw:commentRss>
    
            <category scheme="http://www.zeilenwechsel.de/it/categories/Browser-Bugs" label="Browser Bugs" term="Browser Bugs" />
            <category scheme="http://www.zeilenwechsel.de/it/categories/jQuery" label="jQuery" term="jQuery" />
    
        <id>http://www.zeilenwechsel.de/it/articles/2/guid.html</id>
        <title type="html">jQuery gets the document size wrong in IE</title>
        <content type="xhtml" xml:base="http://www.zeilenwechsel.de/it/">
            <div xmlns="http://www.w3.org/1999/xhtml">
                <p><a href="http://jquery.com/">jQuery</a> functions provide a convenient shorthand for retrieving information from the DOM. There is no need to think about browser-specific quirks – mostly. Here is a case where it doesn't work out.</p>

<p>Retrieving the size of a document should be a straightforward affair: a simple <code>$( document ).width()</code> or <code>$( document ).height()</code> call, and you are done. Yet in IE, these calls often return a size just a little bit too large. Sometimes they are off by as little as 4px, sometimes the difference accounts for the width of the scroll bar, or the scrollbar plus 4px. This bug has been around for a long time. As of jQuery 1.6.1, it is still with us, and it looks like it's going to <a href="http://bugs.jquery.com/ticket/9331">stay that way</a>.</p>

<p>The actual size of the mismatch depends on the document layout and the version of IE. All IE browsers up to and including IE9 are affected, with the curious exception of IE7. The exact workings of this bug tell a little about the document properties used by jQuery and what they actually contain.</p>

<h2>Where it went wrong</h2>

<p>jQuery determines the document width and height by querying these properties and using the largest value:</p>

<ul>
<li><code>d.dE.clientWidth</code>, <code>d.dE.clientHeight</code></li>
<li><code>d.dE.scrollWidth</code>, <code>d.dE.scrollHeight</code></li>
<li><code>body.scrollWidth</code>, <code>body.scrollHeight</code></li>
<li><code>d.dE.offsetWidth</code>, <code>d.dE.offsetHeight</code></li>
<li><code>body.offsetWidth</code>, <code>body.offsetHeight</code></li>
</ul>

<p>The ones mentioned first, the <code>d.dE.client*</code> properties, contain the size of the viewport. The others reflect the content of the document. The bug occurs because the offset properties, <code>d.dE.offset*</code>, will occasionally return a value which is larger than both the viewport and the content area.</p>

<h2>Under the hood</h2>

<p>The <code>d.dE.offset*</code> property is based on the viewport size. In most versions of IE, it also includes elements of the browser chrome, most notably the scrollbar.</p>

<p>This is not an issue as long as there aren’t any scrollbars, of course. Both <code>client*</code> and <code>offset*</code> will return the size of the viewport, as they should. Equally, there won't be a problem if the document is significantly larger than the viewport, in width as well as height. Yes, scrollbars will interfere with <code>d.dE.offset*</code>. But that doesn’t matter because the document covers a much larger area, pushed out by the actual content of the page.</p>

<div class="serendipity_imageComment_left" style="width: 276px"><div class="serendipity_imageComment_img"><!-- s9ymdb:6 --><img class="serendipity_image_left" width="276" height="160" src="http://www.zeilenwechsel.de/it/uploads/pics/2011/05/ie_offset_border_with_scrollbar.png" title="Document width in IE" alt="Document width in IE" /></div><div class="serendipity_imageComment_txt">Document width in IE</div></div>

<p>But what if the page is spilling out of the viewport at the bottom, while still wrapping its content nicely inside the browser window horizontally? The vertical scrollbar is visible, adding to the width reported by <code>d.dE.offsetWidth</code>. The content doesn't exend underneath it. Suddenly <code>d.dE.offsetWidth</code> has become the largest value - and jQuery happily returns it as the document width, which now includes the scrollbar.</p>

<div class="serendipity_imageComment_right" style="width: 140px"><div class="serendipity_imageComment_img"><!-- s9ymdb:7 --><img class="serendipity_image_left" width="140" height="131" src="http://www.zeilenwechsel.de/it/uploads/pics/2011/05/ie8_offset_border.png" title="2px border in IE8" alt="2px border in IE8" /></div><div class="serendipity_imageComment_txt">2px border in IE8</div></div>

<p>This happens in IE6, IE8 and IE9. In IE6, this situation is actually permanent. Even when the complete page is fitting nicely into the browser window, IE6 will display the vertical scrollbar element (though not the actual scrollbar slider itself - after all, there is nothing to scroll). To add to the confusion, IE6 and IE8 also include a small border around the viewport in the figure returned by the <code>offset*</code> property. It accounts for 2px on each side – thus adding a total of 4px in each dimension. For this reason, in IE8, <code>d.dE.offset*</code> and jQuery will be off by 4px even if scrollbars are absent.</p>

<p>It may be easier to see it in action, though. Here is a <a href="http://www.zeilenwechsel.de/it/samples/document-size-ie/compare-methods.php">demo page</a> to play around with. Just take any of the affected IE versions and watch the numbers change as you resize the window. Another, more basic illustration of the bug can be tweaked in <a href="http://jsfiddle.net/_michael/eUs5W/">jsFiddle</a>.</p>

<p>As mentioned before, IE7 is not part of this particular mess. It appears that <code>d.dE.offset*</code> is just a synonym for <code>d.dE.client*</code> in IE7, so <code>d.dE.offset*</code> doesn’t introduce any new, weird numbers here.</p>

<p>In a nutshell: <em>The <code>d.dE.offset*</code> property includes elements of the browser chrome in most versions of IE and is responsible for jQuery misreporting the document size. In addition, the implementation of this property has changed in every new version of IE. Don’t rely on it, ever.</em></p>

<h2>Duct tape</h2>

<p>The solution, then, is both simple and obvious. <code>d.dE.offset*</code> should not be queried in IE.</p>

<p>I am not sure about other browsers, though. While I'm not aware that using this property would be necessary, I didn’t really look into it, and there problably is a good reason for <code>d.dE.offset*</code> showing up in the jQuery source in the first place.</p>

<p>Anyway, a few lines wrapped in a <a href="http://www.zeilenwechsel.de/it/code/download/jquery-docsize-fix/ie_jquery_document_size.js">jQuery extension</a> can handle all that. They fix the issue in IE and leave the code for all other browsers intact. Use the extension by calling <code>$(document).trueWidth()</code> and <code>$(document).trueHeight()</code>.</p>

<p>So here is the code:</p>

<pre><code><span class="geshiContainer"><span style="color: #009900;">(</span> <span style="color: #003366; font-weight: bold;">function</span> <span style="color: #009900;">(</span> $ <span style="color: #009900;">)</span> <span style="color: #009900;">{</span>
 
    <span style="color: #003366; font-weight: bold;">var</span> getPropIE <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">function</span> <span style="color: #009900;">(</span> <span style="color: #000066;">name</span> <span style="color: #009900;">)</span> <span style="color: #009900;">{</span>
 
        <span style="color: #000066; font-weight: bold;">return</span> Math.<span style="color: #660066;">max</span><span style="color: #009900;">(</span>
            document.<span style="color: #660066;">documentElement</span><span style="color: #009900;">[</span><span style="color: #3366CC;">"client"</span> <span style="color: #339933;">+</span> <span style="color: #000066;">name</span><span style="color: #009900;">]</span><span style="color: #339933;">,</span>
            document.<span style="color: #660066;">documentElement</span><span style="color: #009900;">[</span><span style="color: #3366CC;">"scroll"</span> <span style="color: #339933;">+</span> <span style="color: #000066;">name</span><span style="color: #009900;">]</span><span style="color: #339933;">,</span>
            document.<span style="color: #660066;">body</span><span style="color: #009900;">[</span><span style="color: #3366CC;">"scroll"</span> <span style="color: #339933;">+</span> <span style="color: #000066;">name</span><span style="color: #009900;">]</span>
        <span style="color: #009900;">)</span><span style="color: #339933;">;</span>
 
    <span style="color: #009900;">}</span>
 
    $.<span style="color: #660066;">fn</span>.<span style="color: #660066;">trueWidth</span> <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">(</span><span style="color: #009900;">)</span> <span style="color: #009900;">{</span>  
 
        <span style="color: #000066; font-weight: bold;">return</span> <span style="color: #009900;">(</span> <span style="color: #009900;">(</span> $.<span style="color: #660066;">browser</span>.<span style="color: #660066;">msie</span> <span style="color: #339933;">&amp;&amp;</span> <span style="color: #000066; font-weight: bold;">this</span>.<span style="color: #660066;">get</span><span style="color: #009900;">(</span><span style="color: #009900;">)</span><span style="color: #009900;">[</span><span style="color: #CC0000;">0</span><span style="color: #009900;">]</span>.<span style="color: #660066;">nodeType</span> <span style="color: #339933;">===</span> <span style="color: #CC0000;">9</span> <span style="color: #009900;">)</span> <span style="color: #339933;">?</span> getPropIE<span style="color: #009900;">(</span> <span style="color: #3366CC;">'Width'</span> <span style="color: #009900;">)</span> <span style="color: #339933;">:</span> <span style="color: #000066; font-weight: bold;">this</span>.<span style="color: #660066;">width</span><span style="color: #009900;">(</span><span style="color: #009900;">)</span> <span style="color: #009900;">)</span><span style="color: #339933;">;</span>
 
    <span style="color: #009900;">}</span><span style="color: #339933;">;</span>
 
    $.<span style="color: #660066;">fn</span>.<span style="color: #660066;">trueHeight</span> <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">(</span><span style="color: #009900;">)</span> <span style="color: #009900;">{</span>  
 
        <span style="color: #000066; font-weight: bold;">return</span> <span style="color: #009900;">(</span> <span style="color: #009900;">(</span> $.<span style="color: #660066;">browser</span>.<span style="color: #660066;">msie</span> <span style="color: #339933;">&amp;&amp;</span> <span style="color: #000066; font-weight: bold;">this</span>.<span style="color: #660066;">get</span><span style="color: #009900;">(</span><span style="color: #009900;">)</span><span style="color: #009900;">[</span><span style="color: #CC0000;">0</span><span style="color: #009900;">]</span>.<span style="color: #660066;">nodeType</span> <span style="color: #339933;">===</span> <span style="color: #CC0000;">9</span> <span style="color: #009900;">)</span> <span style="color: #339933;">?</span> getPropIE<span style="color: #009900;">(</span> <span style="color: #3366CC;">'Height'</span> <span style="color: #009900;">)</span> <span style="color: #339933;">:</span> <span style="color: #000066; font-weight: bold;">this</span>.<span style="color: #660066;">height</span><span style="color: #009900;">(</span><span style="color: #009900;">)</span> <span style="color: #009900;">)</span><span style="color: #339933;">;</span>
 
    <span style="color: #009900;">}</span><span style="color: #339933;">;</span>
 
<span style="color: #009900;">}</span> <span style="color: #009900;">)</span><span style="color: #009900;">(</span> jQuery <span style="color: #009900;">)</span><span style="color: #339933;">;</span></span>
</code></pre>

<p>As you can see, the code is relying on <a href="http://jqapi.com/#p=jQuery.browser">browser detection</a>. This technique is frowned upon by the majority of web developers, and <a href="http://jibbering.com/faq/notes/detect-browser/#bdFailS">with good reason</a>. Yet here we seem to have one of the few cases where the far superior alternative, <a href="http://peter.michaux.ca/articles/feature-detection-state-of-the-art-browser-scripting">feature detection</a>, doesn't lead anywhere:</p>

<ul>
<li><a href="http://ejohn.org/blog/bad-object-detection/">Object detection</a> doesn't work. The relevant objects are common to IE and other browsers, so they can’t be used to separate the lot. </li>
<li>It seems there is no easy way of simulating the behaviour on the fly, e.g. by creating some inline HTML and examining its properties. The problem here is that the bug doesn't occur with any DOM elements other than the document element itself.</li>
</ul>

<p>Relying on <a href="http://jqapi.com/#p=jQuery.browser">$.browser.msie</a> isn’t the most attractive thing to do, but another option isn't immediately obvious - that is, to me. Should you have an idea, I'd appreciate if you leave a comment. Anyway, sniffing out IE is still a huge improvement over the alternative, which is leaving the bug unfixed.</p>

<h2>Yes, finally, the download</h2>

<p>But perhaps you aren't interested in that kind of debate and just want to <a href="http://www.zeilenwechsel.de/it/code/download/jquery-docsize-fix/ie_jquery_document_size.js">download the extension</a>. When it is loaded, just call <code>$(document).trueWidth()</code> and <code>$(document).trueHeight()</code> instead of the native jQuery methods.</p>

<p>To find out if, at some point, the issue will be fixed in jQuery itself, have a look at the <a href="http://bugs.jquery.com/ticket/9331">somewhat unpromising ticket</a> in the <a href="http://bugs.jquery.com/">jQuery bug tracker</a>. Don't hold your breath.</p>
 
            </div>
        </content>
        <dc:subject>DOM</dc:subject>
<dc:subject>Internet Explorer</dc:subject>
<dc:subject>Javascript</dc:subject>
<dc:subject>jQuery</dc:subject>

    </entry>
    <entry>
        <link href="http://www.zeilenwechsel.de/it/articles/1/Running-.php-files-from-the-context-menu.html" rel="alternate" title="Running .php files from the context menu" />
        <author>
            <name>_ michael</name>
                    </author>
    
        <published>2011-05-04T23:28:15Z</published>
        <updated>1970-01-01T00:00:00Z</updated>
        <wfw:comment>http://www.zeilenwechsel.de/it/wfwcomment.php?cid=1</wfw:comment>
    
        <slash:comments>2</slash:comments>
        <wfw:commentRss>http://www.zeilenwechsel.de/it/rss.php?version=atom1.0&amp;type=comments&amp;cid=1</wfw:commentRss>
    
            <category scheme="http://www.zeilenwechsel.de/it/categories/PHP" label="PHP" term="PHP" />
            <category scheme="http://www.zeilenwechsel.de/it/categories/PHPUnit" label="PHPUnit" term="PHPUnit" />
    
        <id>http://www.zeilenwechsel.de/it/articles/1/guid.html</id>
        <title type="html">Running .php files from the context menu</title>
        <content type="xhtml" xml:base="http://www.zeilenwechsel.de/it/">
            <div xmlns="http://www.w3.org/1999/xhtml">
                <p>In a GUI-driven operating system like Windows, where you don't hang around the command line that much, it is sometimes useful to run a PHP file from its context menu in Windows Explorer.</p>

<p>That's easy to set up. But a reasonably smart handler should perhaps not hand the file over to PHP blindly. <a href="https://github.com/sebastianbergmann/phpunit/">PHPUnit</a> test cases need a different treatment, so the handler could try to detect them and pass them on to PHPUnit instead.</p>

<p>Here's a simple script, <a href="http://www.zeilenwechsel.de/it/code/download/php-command-line-runner/php-cli.bat">php-cli.bat</a>, that does just that. Test files are identified through convention, not introspection: If the filename ends in "...test.php", PHPUnit will get to handle it. All other files are passed on to PHP. Both PHP and PHPUnit are expected to be in your PATH.</p>

<p>This is what the script does:</p>

<pre><code><span class="geshiContainer"><span style="color: #808080; font-style: italic;">@REM PHP CLI runner, incorporating a PHPUnit test runner.</span>
<span style="color: #808080; font-style: italic;">    
@REM   o Expects a filepath as first argument.</span>
<span style="color: #808080; font-style: italic;">@REM   o An ordinary file will be passed on to PHP. If the file is a test, it</span>
<span style="color: #808080; font-style: italic;">@REM     will be run through PHPUnit.</span>
<span style="color: #808080; font-style: italic;">@REM   o Test files are identified by their basename ending in 'Test'.</span>
<span style="color: #808080; font-style: italic;">@REM   o PHP and PHPUnit must be found in the PATH.</span>
 
<span style="color: #33cc33;">@</span><a href="http://www.ss64.com/nt/echo.html"><span style="color: #b1b100; font-weight: bold;">echo</span></a> off
 
<a href="http://www.ss64.com/nt/set.html"><span style="color: #b1b100; font-weight: bold;">set</span></a> separator=________________________________________________________________________________
<span style="color: #808080; font-style: italic;">
@REM Get the filename as basename without extension</span>
<a href="http://www.ss64.com/nt/set.html"><span style="color: #b1b100; font-weight: bold;">set</span></a> filename=<span style="color: #33cc33;">%</span><span style="color: #448888;">~n1</span>
<a href="http://www.ss64.com/nt/set.html"><span style="color: #b1b100; font-weight: bold;">set</span></a> filepath=<span style="color: #33cc33;">%</span><span style="color: #448888;">~f1</span>
<a href="http://www.ss64.com/nt/set.html"><span style="color: #b1b100; font-weight: bold;">set</span></a> scriptdir=<span style="color: #33cc33;">%</span><span style="color: #448888;">~dp1</span>
 
<a href="http://www.ss64.com/nt/cd.html"><span style="color: #b1b100; font-weight: bold;">cd</span></a> /d <span style="color: #33cc33;">%</span><span style="color: #448888;">scriptdir</span><span style="color: #33cc33;">%</span>
<span style="color: #808080; font-style: italic;">
@REM check if the filename ends in "Test"</span>
<a href="http://www.ss64.com/nt/set.html"><span style="color: #b1b100; font-weight: bold;">set</span></a> suffix=<span style="color: #33cc33;">%</span><span style="color: #448888;">filename:~-4,4</span><span style="color: #33cc33;">%</span>
<a href="http://www.ss64.com/nt/if.html"><span style="color: #00b100; font-weight: bold;">if</span></a> "<span style="color: #33cc33;">%</span><span style="color: #448888;">suffix</span><span style="color: #33cc33;">%</span>" == "Test" <a href="http://www.ss64.com/nt/goto.html"><span style="color: #00b100; font-weight: bold;">GOTO</span></a> :<span style="color: #b100b1; font-weight: bold;">IS_TESTFILE</span>
<span style="color: #808080; font-style: italic;">
@REM not a testfile, run through PHP</span>
<a href="http://www.ss64.com/nt/echo.html"><span style="color: #b1b100; font-weight: bold;">echo</span></a> Running <span style="color: #33cc33;">%</span><span style="color: #448888;">filepath</span><span style="color: #33cc33;">%</span> ...
<a href="http://www.ss64.com/nt/echo.html"><span style="color: #b1b100; font-weight: bold;">echo</span></a> <span style="color: #33cc33;">%</span><span style="color: #448888;">separator</span><span style="color: #33cc33;">%</span>
<a href="http://www.ss64.com/nt/echo.html"><span style="color: #b1b100; font-weight: bold;">echo</span></a>.
 
php <span style="color: #33cc33;">%</span><span style="color: #448888;">*</span>
<a href="http://www.ss64.com/nt/echo.html"><span style="color: #b1b100; font-weight: bold;">echo</span></a>.
<span style="color: #808080; font-style: italic;">
@REM keep the window open after PHP has run</span>
cmd /K
 
<a href="http://www.ss64.com/nt/goto.html"><span style="color: #00b100; font-weight: bold;">GOTO</span></a> :<span style="color: #b100b1; font-weight: bold;">END</span>
 
:<span style="color: #b100b1; font-weight: bold;">IS_TESTFILE</span>
<span style="color: #808080; font-style: italic;">
@REM this is a testfile, use PHPUnit</span>
<a href="http://www.ss64.com/nt/echo.html"><span style="color: #b1b100; font-weight: bold;">echo</span></a> Running tests <a href="http://www.ss64.com/nt/in.html"><span style="color: #00b100; font-weight: bold;">in</span></a> <span style="color: #33cc33;">%</span><span style="color: #448888;">filepath</span><span style="color: #33cc33;">%</span> ...
<a href="http://www.ss64.com/nt/echo.html"><span style="color: #b1b100; font-weight: bold;">echo</span></a> <span style="color: #33cc33;">%</span><span style="color: #448888;">separator</span><span style="color: #33cc33;">%</span>
<a href="http://www.ss64.com/nt/echo.html"><span style="color: #b1b100; font-weight: bold;">echo</span></a>.
 
START /B phpunit <span style="color: #33cc33;">%</span><span style="color: #448888;">*</span>
 
:<span style="color: #b100b1; font-weight: bold;">END</span></span>
</code></pre>

<p>In case you wonder about the <code>%~n1</code> oddities, these are actually <code>%1</code> <a href="http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/percent.mspx?mfr=true">placeholders with modifiers</a>, whereas <code>set suffix=%filename:~-4,4%</code> uses a <a href="http://www.dostips.com/DtTipsStringManipulation.php">directive for string manipulation</a>.</p>

<h2>Setting it up</h2>

<p>There are several ways to add the batch script to the context menu of .php files.</p>

<h3>In the "Open with ..." flyout menu (Windows 7)</h3>

<p>Just put the batch file wherever you like it to be "installed". Next, right-click on any PHP file, select "Open with ..." &gt; "Choose default program" &gt; "Browse" and pick the script. Before you hit OK, don't forget to <strong>un</strong>check "Always use the selected program to open this kind of file" unless that's really what you want.</p>

<p>As soon as this is done, php-cli.bat will become a permanent option in the "Open with" list for PHP files.</p>

<h3>At the top level of the context menu (Windows 7)</h3>

<p>You'll have to edit the Registry for that. (Disclaimer: Don't do that unless you know what you are doing, are aware of the risks involved and take adequate precautions.)</p>

<p>Assuming you have registered .php files before as a PHPFile type (or some app has done it for you),</p>

<ul>
<li>open HKEY_CLASSES_ROOT\PHPFile\shell\</li>
<li>Add a subkey named "Run with PHP".</li>
<li>Set the default value to the text you want to appear in the context menu, e.g. "Run with PHP"</li>
<li>Inside the new key, add a subkey named "Command"</li>
<li>Set the default value to <code>"C:\path\to\php-cli.bat" "%1" %*</code> (including the quotes), e.g. <code>"C:\Program Files (x86)\php-extras\php-cli.bat" "%1" %*</code></li>
</ul>

<p><!-- s9ymdb:2 --><img class="serendipity_image_left" width="778" height="139" src="http://www.zeilenwechsel.de/it/uploads/pics/2011/05/register-php-extension-handler.png" title="Register PHP Extension Handler" alt="Register PHP Extension Handler" /></p>

<p>And you are done. If you are interested, a generic description of registering a context menu handler is available at MSDN in a <a href="http://msdn.microsoft.com/en-us/library/cc144101(v=VS.85).aspx">basic</a> and <a href="http://msdn.microsoft.com/en-us/library/cc144171(v=vs.85).aspx">more elaborate</a> version.</p>

<h3>Other Windows flavours</h3>

<p>In Windows Vista, the procedure should be roughly the same (I have not tried it, though). If you are still using Windows XP, adding an entry to the context menu works <a href="http://www.annoyances.org/exec/show/article02-026">a little differently</a>.</p>

<p><a href="http://www.zeilenwechsel.de/it/code/download/php-command-line-runner/php-cli.bat">Download php-cli.bat</a></p>
 
            </div>
        </content>
        <dc:subject>PHP</dc:subject>
<dc:subject>PHPUnit</dc:subject>
<dc:subject>Testing</dc:subject>
<dc:subject>Windows</dc:subject>

    </entry>

</feed>
