tag:blogger.com,1999:blog-36537453354758758632024-03-14T05:06:14.800+01:00A Basketful Of PapayasThWhttp://www.blogger.com/profile/12009821760062911474noreply@blogger.comBlogger107125tag:blogger.com,1999:blog-3653745335475875863.post-28218557519208101362017-10-03T17:30:00.000+02:002017-10-04T10:55:03.436+02:00FluentDOM 7.0, The Next StepFluentDOM 7.0 is out, so what has changed? Well, the FluentDOM namespace got a little crowded so I moved all the DOM child classes into <tt>FluentDOM\DOM</tt>, made the Creator a top level class and collected the utility classes. If you're updating you might need to change some imports. FluentDOM now requires PHP 7 and uses scalar type hints. In other words, lots of cleanup.<br />
<h3>
FluentDOM\XMLReader\SiblingIterator</h3>
Large XML files usually consist of a list element with many record elements as its children. The whole list is to large to load into memory, but the records are small enough.<br />
<br />
The SiblingIterator takes a XMLReader, a tag name and a filter callback. It matches the tag name and executes the filter callback. If the tag name matches and the filter callback returns TRUE it will expand the node into DOM. After the first match it will only consider following siblings. This allows you to improve the read performance.<br />
<br />
Here is an example that read a XML sitemap including video information.<br />
<br />
<pre>$reader = new FluentDOM\XMLReader();
$reader->open($sitemapFile);
$reader->registerNamespace(
's', 'http://www.sitemaps.org/schemas/sitemap/0.9'
);
$reader->registerNamespace(
'v', 'http://www.google.com/schemas/sitemap-video/1.1'
);
foreach (new FluentDOM\XMLReader\SiblingIterator($reader, 's:url') as $url) {
/** @var FluentDOM\DOM\Element $url */
var_dump(
[
$url('string(v:video/v:title)'),
$url('string(s:loc)')
]
);
}</pre>
<h3>
FluentDOM\XMLWriter::collapse()</h3>
FluentDOM 7.0 adds a
collapse() method to XMLWriter. It is the missing opposite of
XMLReader::expand(). Using the two methods allows you to work with large
XML files in a really easy way.<br />
<br />
The collapse() method
takes any DOM node or node list and will write it to the output stream.
You can use the extended DOM classes, FluentDOM\Creator or
FluentDOM\Query to create the record node.<br />
<br />
<pre>$writer = new FluentDOM\XMLWriter();
$writer->openURI('php://stdout');
$writer->registerNamespace(
'', 'http://www.sitemaps.org/schemas/sitemap/0.9'
);
$writer->registerNamespace(
'video', 'http://www.google.com/schemas/sitemap-video/1.1'
);
$writer->setIndent(2);
$writer->startDocument();
$writer->startElement('urlset');
$writer->writeAttribute(
'xmlns:video', 'http://www.google.com/schemas/sitemap-video/1.1'
);
$_ = FluentDOM::create();
$_->registerNamespace(
'', 'http://www.sitemaps.org/schemas/sitemap/0.9'
);
$_->registerNamespace(
'video', 'http://www.google.com/schemas/sitemap-video/1.1'
);
foreach ($videos as $video) {
$writer->collapse(
$_(
'url',
$_('loc', $video['url']),
$_(
'video:video',
$_('video:title', $video['title'])
)
)
);
}
$writer->endElement();
$writer->endDocument();</pre>
<br />
XMLWriter::setAttribute() recognizes if you write an namespace definition so it will not add it to descendant nodes.<br />
<h3>
Put Together</h3>
If you combine the expand iterator with collapse you can easily write mappers that can consume large XML files. You can basically use each record as a separate DOM document.<br />
<br />
For example you can use it to merge XML documents and change the namespaces:<br />
<br />
<pre>$writer = new \FluentDOM\XMLWriter();
$writer->openURI('php://stdout');
$writer->registerNamespace('p', 'urn:persons');
$writer->setIndent(2);
$writer->startDocument();
$writer->startElement('p:persons');
// iterate the example sources
foreach ($data as $sourceFile) {
// load the source into a reader
$reader = new \FluentDOM\XMLReader();
$reader->open($sourceFile);
// iterate the person elements
$persons = new FluentDOM\XMLReader\SiblingIterator($reader, 'person');</pre>
<pre> foreach ($persons as $person) {
// use the transformer to move the nodes into the namespace
$writer->collapse(
new \FluentDOM\Transformer\Namespaces\Replace(
$person,
// namespaces to replace
['' => 'urn:persons', 'urn:example' => 'urn:persons'],
// prefix for target namespace
['urn:persons' => 'p']
)
);
}
}
$writer->endElement();
$writer->endDocument();</pre>
ThWhttp://www.blogger.com/profile/12009821760062911474noreply@blogger.com0tag:blogger.com,1999:blog-3653745335475875863.post-23561812325789808892017-07-02T17:02:00.000+02:002017-07-02T17:02:20.678+02:00FluentDOM 6.1 released - Improvements<p>Release: <a href="https://github.com/FluentDOM/FluentDOM/releases/tag/6.1.0">FluentDOM 6.1.0</a> </p>
<h2 id="multibyte-html">MultiByte HTML</h2>
<p>Thanks to some issues reported by Kyle Tse the multibyte handling for HTML was improved. It should now work properly. The HTML loader can read the encoding/charset from meta tags or you can specify as an loader option. The default is UTF-8. <tt>FluentDOM\Document::saveHTML()</tt> has got some additional logic as well.</p>
<h2 id="xmlreaderxmlwriter">XMLReader/XMLWriter</h2>
<p>If you need to handle huge XML files, the XMLReader and XMLWriter APIs are the way to do it. Well you could try using SAX, but believe me THAT is no fun. XMLReader and XMLWriter are nice APIs by itself, so FluentDOM adds only slight changes for namespace handling.</p>
<h3 id="xmlreaderreadxmlreadernext">XMLReader::read()/XMLReader::next()</h3>
<p>Of the two traversing methods, only <tt>next()</tt> allows to specify a local name as a condition. FluentDOM extends the signature of both methods to allow for a tag name and a namespace URI. As a result the source reading an XML with namespaces can be simplified: </p>
<pre class="prettyprint"><code class="language-php hljs "><span class="hljs-variable">$sitemapUri</span> = <span class="hljs-string">'http://www.sitemaps.org/schemas/sitemap/0.9'</span>;
<span class="hljs-variable">$reader</span> = <span class="hljs-keyword">new</span> FluentDOM\XMLReader();
<span class="hljs-variable">$reader</span>->open(<span class="hljs-variable">$file</span>);
<span class="hljs-keyword">if</span> (<span class="hljs-variable">$reader</span>->read(<span class="hljs-string">'url'</span>, <span class="hljs-variable">$sitemapUri</span>)) {
<span class="hljs-keyword">do</span> {
<span class="hljs-comment">//...</span>
} <span class="hljs-keyword">while</span> (<span class="hljs-variable">$reader</span>->next(<span class="hljs-string">'url'</span>, <span class="hljs-variable">$sitemapUri</span>));
}</code></pre>
<h3 id="xmlreaderregisternamespace">XMLReader::registerNamespace()</h3>
<p>Additionally you can register namespaces on the XMLReader object itself. This allows it resolve namespace prefixes in tag name arguments.</p>
<p>Namespace definitions will be propagated to an <tt>FluentDOM\Document</tt> instance created by <tt>FluentDOM\XMLReader::expand()</tt>.</p>
<pre class="prettyprint"><code class="language-php hljs "><span class="hljs-variable">$reader</span> = <span class="hljs-keyword">new</span> FluentDOM\XMLReader();
<span class="hljs-variable">$reader</span>->open(<span class="hljs-variable">$file</span>);
<span class="hljs-variable">$reader</span>->registerNamespace(<span class="hljs-string">'s'</span>, <span class="hljs-string">'http://www.sitemaps.org/schemas/sitemap/0.9'</span>);
<span class="hljs-keyword">if</span> (<span class="hljs-variable">$reader</span>->read(<span class="hljs-string">'s:url'</span>)) {
<span class="hljs-keyword">do</span> {
<span class="hljs-variable">$url</span> = <span class="hljs-variable">$reader</span>->expand();
var_dump(
<span class="hljs-variable">$url</span>(<span class="hljs-string">'string(s:loc)'</span>)
);
} <span class="hljs-keyword">while</span> (<span class="hljs-variable">$reader</span>->next(<span class="hljs-string">'s:url'</span>));
}</code></pre>
<h3 id="xmlwriterregisternamespace">XMLWriter::registerNamespace()</h3>
<p>The same registration is possible on an <tt>FluentDOM\XMLWriter</tt>. It keeps track track of the namespaces defined in the current context and avoid adding unnecessary definitions to the output (<a href="https://bugs.php.net/bug.php?id=74491">PHP Bug</a>).</p>
<p>XMLWriter has many methods that have a tag name argument and this change allows all of them to become namespace aware.</p>
<pre class="prettyprint"><code class="language-php hljs "><span class="hljs-variable">$writer</span> = <span class="hljs-keyword">new</span> FluentDOM\XMLWriter();
<span class="hljs-variable">$writer</span>->openURI(<span class="hljs-string">'php://stdout'</span>);
<span class="hljs-variable">$writer</span>->registerNamespace(<span class="hljs-string">''</span>, <span class="hljs-string">'http://www.sitemaps.org/schemas/sitemap/0.9'</span>);
<span class="hljs-variable">$writer</span>->setIndent(<span class="hljs-number">2</span>);
<span class="hljs-variable">$writer</span>->startDocument();
<span class="hljs-variable">$writer</span>->startElement(<span class="hljs-string">'urlset'</span>);
<span class="hljs-keyword">foreach</span> (<span class="hljs-variable">$urls</span> <span class="hljs-keyword">as</span> <span class="hljs-variable">$url</span>) {
<span class="hljs-variable">$writer</span>->startElement(<span class="hljs-string">'url'</span>);
<span class="hljs-variable">$writer</span>->writeElement(<span class="hljs-string">'loc'</span>, <span class="hljs-variable">$url</span>[<span class="hljs-string">'href'</span>]);
<span class="hljs-comment">// ...</span>
<span class="hljs-variable">$writer</span>->endElement();
}
<span class="hljs-variable">$writer</span>->endElement();
<span class="hljs-variable">$writer</span>->endDocument();</code></pre>ThWhttp://www.blogger.com/profile/12009821760062911474noreply@blogger.com0tag:blogger.com,1999:blog-3653745335475875863.post-30208992471602957822016-12-24T20:29:00.000+01:002016-12-27T14:06:46.864+01:00FluentDOM 6.0 - What's New<p>
<a href="https://github.com/FluentDOM/FluentDOM/releases/tag/6.0.0">FluentDOM 6.0</a> is released.
</p>
<p>
So a major version jump means that here are backwards compatibility breaks and major new features. File loading has
changed to improve security. <tt>FluentDOM\Query</tt> has got a major overhaul to improve the integration of alternative
formats. This affected the interfaces and usage. I tried to keep the breaks to a minimum and easily fixable.
</p>
<h2>Loading Files</h2>
<p>
To load a file you will now have to explicitly allow it using the options argument. Here are two options.
<tt>FluentDOM\Loader\Options::ALLOW_FILE</tt> basically restores the previous behaviour. The loader still
checks if it is a file or string and will load both. <tt>FluentDOM\Loader\Options::IS_FILE</tt> means that only
a file will be loaded. This has to be implemented into to respective loader. So if you notice that some loader
behaves differently, please drop me a note.
</p>
<p>
All loading functions allow you to provide the option.
</p>
<pre><code class="language-php">$fd = FluentDOM($file, 'xml', [ FluentDOM\Loader\Options::ALLOW_FILE => TRUE ]);
$document = FluentDOM::load(
$file, 'xml', [ FluentDOM\Loader\Options::IS_FILE => TRUE ]
);</code></pre>
<h2>Fragment Loading / Query Content Types</h2>
<p>
Several loaders now support fragment loading. It is used by methods like <tt>FluentDOM\\Query::append()</tt>.
It allows the Query API to keep the content type that you loaded. So if you load HTML, the fragment are expected to
be html, if you load XML the fragments are expected to be XML, if you load JSON ... well you get the picture. :-)
</p>
<pre><code class="language-php">$fd = FluentDOM('<form></form>', 'text/html')->find('//form');
$fd->append('<input type="text" name="example">');
echo $fd;</code></pre>
<p>
You can change the behaviour by setting the content type. It works with additional loaders, so if you install
<tt>fluentdom/html5</tt> you get transparent support for HTML5.
</p>
<pre><code class="language-php">$fd = FluentDOM('<form></form>', 'text/html5')->find('//html:form');
$fd->append('<input type="text" name="example">');
echo $fd;</code> </pre>
<p> The changes mean that all existing loaders need to be updated for FluentDOM 6.0.</p>
<h2>Serializers</h2>
<p>
Serializers need to register itself on the FluentDOM class. It allows the <tt>FluentDOM\Query</tt> objects
to output the same content type it loaded. Additionally you can use
<tt>FluentDOM::getSerializerFactories()->createSerializer();</tt> to get a serializer for a node by content type.
I am thinking about adding something like a <tt>FluentDOM::save()</tt> function as a shortcut for that,
but I am not sure about the name and implementation yet. If you have a suggestion please add it to the
<a href="https://github.com/FluentDOM/FluentDOM/issues/56">Issue</a>.
</p>
<h2>Replace Whole Text</h2>
<p>
Character nodes (Text nodes and CDATA sections) have a property <tt>$wholeText</tt>.
It returns the text content and the sibling character nodes. It even resolves entity references for that.
The property is read only, but DOM Level 3 specifies a method <tt>replaceWholeText()</tt> as a write method
for it. FluentDOM 6.0 implements that method in its extended DOM classes now.
</p>
<pre><code class="language-php">$document = new FluentDOM\Document();
$document->loadXML(
'<!DOCTYPE p ['."\n".
' <!ENTITY t "world">'."\n".
']>'."\n".
'<p>Hello &t;<br/>, Hello &t;</p>'
);
/** @var \FluentDOM\Text $text */
$text = $document->documentElement->firstChild;
$text->replaceWholeText('Hi universe');
echo $document->saveXML();</code></pre>
<h2>Examples</h2>
<p>The examples directory did grow a little confusing over the years. I restructured and refactored it.
Some examples got removed, because the features are shown by newer examples.</p>
<h2>What's Next?</h2>
<p>
I still have to updated some of the plugin repositories (loaders, serializers) and add some more
documentation to the wiki. After that I plan to take a look into DOM Level 4. If you have suggestions please
add a ticket to the <a href="https://github.com/FluentDOM/FluentDOM/issues">issue tracker</a>
</p>ThWhttp://www.blogger.com/profile/12009821760062911474noreply@blogger.com0tag:blogger.com,1999:blog-3653745335475875863.post-12951995963829481082015-05-30T20:51:00.000+02:002015-09-09T12:08:51.807+02:00Surface Pro 3 - VersatilityThe SP3 is the most useful laptop I ever had and the reason is not performance it is versatility.<br />
<br />
<h2 style="clear: both;">
One: Notes</h2>
<a href="http://3.bp.blogspot.com/-HrpXLe1u2qo/VWoB1ZEjPeI/AAAAAAAAD1E/62K2I4legqk/s1600/IMG_20150530_174851.jpg" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="200" src="http://3.bp.blogspot.com/-HrpXLe1u2qo/VWoB1ZEjPeI/AAAAAAAAD1E/62K2I4legqk/s200/IMG_20150530_174851.jpg" width="165" /></a>
The Surface replaces a legal pad in this case. The battery is strong enough to last even a long meeting (>8h). The stylus is precise. Sensitivity was an issue at first, but the configuration app fixed it.<br />
<h2 style="clear: both;">
Two: Media Tablet</h2>
<a href="http://3.bp.blogspot.com/-5xfxjnK87DM/VWoB1TgBs_I/AAAAAAAAD1E/S9mHOUUETx0/s1600/IMG_20150530_175404.jpg" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="134" src="http://3.bp.blogspot.com/-5xfxjnK87DM/VWoB1TgBs_I/AAAAAAAAD1E/S9mHOUUETx0/s200/IMG_20150530_175404.jpg" width="200" /></a>
Movies, web browsing, reading - I do that mostly in tablet mode using the kickstand to prop it up. If you want better sound, connect a Bluetooth speaker/headphones.<br />
<br />
<h2 style="clear: both;">
Three: Presentations</h2>
<a href="http://1.bp.blogspot.com/-ogQJoD0PLS4/VWoB1f6izZI/AAAAAAAAD1E/J0Xz9Ph8Auo/s1600/IMG_20150530_180255.jpg" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="136" src="http://1.bp.blogspot.com/-ogQJoD0PLS4/VWoB1f6izZI/AAAAAAAAD1E/J0Xz9Ph8Auo/s200/IMG_20150530_180255.jpg" width="200" /></a>
PDF works as well as any Windows presentation program. MDP adapters allow to connect to VGA or HDMI. If you forgot your own, just ask another speaker with a Macbook.<br />
<br />
You want to control the presentation wireless? The Kensington Presentair is a nice Bluetooth presenter with a Micro USB loading port.<br />
<br />
Also, the stylus allows flip chart like presentations.<br />
<br />
<h2 style="clear: both;">
Four: Mobile Work</h2>
<a href="http://2.bp.blogspot.com/-ccDW9kAnWSk/VWoB1WvrWPI/AAAAAAAAD14/pZzCVUpilko/s1600/IMG_20150530_180837.jpg" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="171" src="http://2.bp.blogspot.com/-ccDW9kAnWSk/VWoB1WvrWPI/AAAAAAAAD14/pZzCVUpilko/s200/IMG_20150530_180837.jpg" width="200" /></a>
It is light enough to carry around and the display has a high resolution - large enough to use an IDE comfortably. The battery last long enough that I do not carry the power supply. It stays at home or in the hotel room. If I expect to need more time, I take a battery pack with me.<br />
<br />
The Logitech T630 mouse does not take much space in the bag, has an Micro USB loading port ans support gestures.<br />
<br />
A small UMTS stick provides connectivity at the moment, but I hope for LTE in the Surface Pro 4. <br />
<h2 style="clear: both;">
Five: Portable Workstation </h2>
<a href="http://1.bp.blogspot.com/-WaSOstxJtBg/VWoB1bJTnvI/AAAAAAAAD1s/QVhPholj_oM/s1600/IMG_20150530_182313.jpg" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="128" src="http://1.bp.blogspot.com/-WaSOstxJtBg/VWoB1bJTnvI/AAAAAAAAD1s/QVhPholj_oM/s200/IMG_20150530_182313.jpg" width="200" /></a>
Working in a customers office for some days allows for a larger setup. The USB port on the SP3 is not powerful enough for an external monitor, but the one in the power supply is. I use a MS168B+ connected to a small USB hub with a Micro USB port for power. The hub provides LAN and an SD-Card reader, too. Typically I set it up with the SP3 power supply, but it works with the battery pack. The wireless adapter converts the type cover into an Bluetooth keyboard.<br />
<br />
Maybe Microsoft will fix the USB port power issue in the next Surface Pro.<br />
<h2 style="clear: both;">
Six: Desktop Workstation</h2>
<a href="http://1.bp.blogspot.com/-EW8mFXJRr7k/VemCJ7_crEI/AAAAAAAAECg/jZnCZ956t2k/s1600/IMG_20150904_132901.jpg" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="101" src="http://1.bp.blogspot.com/-EW8mFXJRr7k/VemCJ7_crEI/AAAAAAAAECg/jZnCZ956t2k/s200/IMG_20150904_132901.jpg" width="200" /></a>
I have two of the docking stations - at work and at home. At home it is connected to an LG ultra widescreen monitor (3440x1440px). At work it is connected to two monitors with the <a href="http://www.evga.com/articles/00815/" target="_blank">EVGA DisplayPort MST Hub</a>. I could connect the monitors directly to the two MDP ports (docking station and SP3). But the whole point of a docking station is to not have to connect separate cables.
ThWhttp://www.blogger.com/profile/12009821760062911474noreply@blogger.com0tag:blogger.com,1999:blog-3653745335475875863.post-21308813696782028232015-02-02T18:51:00.000+01:002015-02-02T19:05:53.371+01:00FluentDOM 5.2 - New Features<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-_zPhs2dM36E/U-H5_igH4YI/AAAAAAAADbY/8X1n18MPc98/s1600/fluentdom_logo_large.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="http://2.bp.blogspot.com/-_zPhs2dM36E/U-H5_igH4YI/AAAAAAAADbY/8X1n18MPc98/s1600/fluentdom_logo_large.png" height="155" width="200" /></a></div>
FluentDOM 5.2 is released and here some new features:<br />
<h2>
FluentDOM::load() </h2>
The new static FluentDOM::load() function allows to use the loaders to get a FluentDOM\Document instance.<br />
<pre style="clear: left; background-color: #141414; color: #f8f8f8; font-family: 'Source Code Pro'; font-size: 14pt;"><span style="background-color: #472c47; color: #7587a6;">$json</span><span style="color: #7587a6;"> </span><span style="color: #cda869;">= </span><span style="color: #9b703f;">FluentDOM</span><span style="color: #cda869;">::</span><span style="color: #9b703f;">load</span>(<span style="background-color: #3c3c57; color: #7587a6;">$json</span>, <span style="color: #8f9d6a;">'text/json'</span>);
<span style="color: #cda869;">echo </span><span style="background-color: #3c3c57; color: #7587a6;">$json</span>(
<span style="color: #8f9d6a;">'string(//phoneNumbers/*[type="home"]/number)'</span>
);</pre>
<h2>
DOM Living Standard</h2>
<br />
A large part of the <a href="https://dom.spec.whatwg.org/#node-tree" rel="nofollow">DOM Living Standard</a> is implemented. It adds properties like $element->firstElementChild and methods like $element->remove().<br />
<br />
Query selectors are not implemented yet. Still thinking about that. If you have an opinion please add a comment to the <a href="https://github.com/FluentDOM/FluentDOM/issues/22" rel="nofollow">issue</a>. <br />
<h2>
Two New Loaders</h2>
<ul>
<li><a href="http://www-01.ibm.com/support/knowledgecenter/SS9H2Y_6.0.0/com.ibm.dp.xi.doc/json_jsonx.html">JSONx</a> </li>
<li>CSV </li>
</ul>
<h2>
Loader Plugins</h2>
It was already possible to use the loaders directly or to set the loaders for a FluentDOM\Query object. It is now possible to register them on the FluentDOM class. This makes them available to the FluentDOM() and FluentDOM::load() functions. <br />
<br />
Additional packages like <a href="https://packagist.org/packages/fluentdom/fluentdom" rel="nofollow">FluentDOM/HTML5</a> can register their loaders with specific content types. Just require the package with Composer and you're ready to go:<br />
<pre style="background-color: #141414; color: #f8f8f8; font-family: 'Source Code Pro'; font-size: 14pt;"><span style="color: #7587a6;">$dom </span><span style="color: #cda869;">= </span><span style="color: #9b703f;">FluentDOM</span><span style="color: #cda869;">::</span><span style="color: #9b703f;">load</span>(<span style="color: #7587a6;">$html</span>, <span style="color: #8f9d6a;">'text/html5'</span>);</pre>
<h2>
FluentDOM\Query::find()</h2>
The behavior of find() has been changed to allow two modes. FIND_MODE_MATCH executes the selector directly. It should be faster and work better with XPath. FIND_MODE_FILTER filters the descendant nodes of the current context - like the jQuery documentation describes it. ThWhttp://www.blogger.com/profile/12009821760062911474noreply@blogger.com0tag:blogger.com,1999:blog-3653745335475875863.post-1474774484036840002014-11-22T14:45:00.000+01:002014-11-22T14:47:44.712+01:00Surface Pro - USB HubThe Surface Pro is a cool device but USB connectivity is one of its weak point. It has a single USB 3 port that is not fully powered. Devices that need the more power like an USB monitor will not work.<br />
<br />
Up until now I used a hub with 3 USB ports and LAN (RJ45). An Y cable connected the USB port on the power supply to provide additional power to the hub.<br />
<br />
I found something better. <br />
<ul>
<li>Really small form factor</li>
<li>2 USB 3 ports, </li>
<li>1 RJ45 LAN port</li>
<li>Card reader (SD and Micro SD)</li>
<li>Micro USB power connector </li>
</ul>
So I loose 1 USB port, but get the SD card reader. The form factor is really nice. The killer feature is the Micro USB port. Finally a powered USB hub that does not need a special power supply. <br />
<br />
Take a look:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-KtdJt1_auZM/VHCPv700JgI/AAAAAAAADfo/kjisCIfJPCc/s1600/IMG_20141120_143331_edited.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-KtdJt1_auZM/VHCPv700JgI/AAAAAAAADfo/kjisCIfJPCc/s1600/IMG_20141120_143331_edited.jpg" height="236" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://1.bp.blogspot.com/-0a_g1iSKy7w/VHCPw_2V2-I/AAAAAAAADfs/t2oL6l8gGeM/s1600/IMG_20141120_143342_edited.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://1.bp.blogspot.com/-0a_g1iSKy7w/VHCPw_2V2-I/AAAAAAAADfs/t2oL6l8gGeM/s1600/IMG_20141120_143342_edited.jpg" height="236" width="320" /></a></div>
<br />
I tested it connected to the Surface Pro power supply and the Asus MB168B+ USB monitor. Works fine. ThWhttp://www.blogger.com/profile/12009821760062911474noreply@blogger.com0tag:blogger.com,1999:blog-3653745335475875863.post-54517157693161134652014-09-20T15:18:00.000+02:002014-09-20T15:21:27.066+02:00FluentDOM 5.1 - New Features<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-_zPhs2dM36E/U-H5_igH4YI/AAAAAAAADbY/8X1n18MPc98/s1600/fluentdom_logo_large.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://2.bp.blogspot.com/-_zPhs2dM36E/U-H5_igH4YI/AAAAAAAADbY/8X1n18MPc98/s1600/fluentdom_logo_large.png" /></a></div>
FluentDOM 5.1 is now available. Here are some of the highlights: <br />
<h2>
Functors </h2>
The classes can now be called as functions to navigate in a DOM with XPath expressions. The following example fetches all link hrefs attributes from an HTML page:<br />
<pre style="background-color: #2b2b2b; color: #e6e1dc; font-family: 'Courier New'; font-size: 16pt;"><span style="background-color: #472c47; color: #d0d0ff;">$dom</span><span style="color: #d0d0ff;"> </span><span style="color: #cc7833;">= new </span>\FluentDOM\Document();
<span style="background-color: #3c3c57; color: #d0d0ff;">$dom</span><span style="color: #cc7833;">-></span><span style="color: #ffc66d;">loadHTMLFile</span>(<span style="color: #a5c261;">'http://fluentdom.org/'</span>);
<span style="color: #d0d0ff;">$links </span><span style="color: #cc7833;">= </span>[];
<span style="color: #cc7833;">foreach </span>(<span style="background-color: #3c3c57; color: #d0d0ff;">$dom</span>(<span style="color: #a5c261;">'//a[@href]</span><span style="color: #a5c261;"><span style="color: #a5c261;">/@href</span>'</span>) <span style="color: #cc7833;">as </span><span style="color: #d0d0ff;">$href</span>) {
<span style="color: #d0d0ff;">$links</span>[] <span style="color: #cc7833;">= (string)</span><span style="color: #d0d0ff;">$href</span>;
}</pre>
<br />
<br />
This works for most of the nodes in a DOM. <br />
<h2>
Creator</h2>
The new Creator class provides short syntax to create DOM nodes. More detailed information can be found in the <a href="https://github.com/FluentDOM/FluentDOM/wiki/Creator">wiki</a>.<br />
<pre style="background-color: #2b2b2b; color: #e6e1dc; font-family: 'Courier New'; font-size: 16pt;"><span style="background-color: #472c47; color: #d0d0ff;">$_</span><span style="color: #d0d0ff;"> </span><span style="color: #cc7833;">= </span><span style="color: white;">FluentDOM</span><span style="color: #cc7833;">::</span><span style="color: #ffc66d;">create</span>();
<span style="color: #cc7833;">echo </span><span style="background-color: #3c3c57; color: #d0d0ff;">$_</span>(
<span style="color: #a5c261;">'ul'</span>,
[<span style="color: #a5c261;">'class' </span>=> <span style="color: #a5c261;">'navigation'</span>],
<span style="background-color: #3c3c57; color: #d0d0ff;">$_</span>(<span style="color: #a5c261;">'li'</span>, <span style="color: #a5c261;">'FluentDOM'</span>)
);</pre>
<h2>
XML To JSON</h2>
Several serializers/loaders for JSON where added. JSONML, Rayfish, BadgerFish and RabbitFish are supported.<br />
<pre style="background-color: #2b2b2b; color: #e6e1dc; font-family: 'Courier New'; font-size: 16pt;"><span style="color: #cc7833;">echo </span><span style="color: #a5c261;">"XML -> JsonML</span><span style="color: #519f50;">\n\n</span><span style="color: #a5c261;">"</span>;
<span style="color: #d0d0ff;">$json </span><span style="color: #cc7833;">= </span><span style="color: #ffc66d;">json_encode</span>(
<span style="color: #cc7833;">new </span>FluentDOM\Serializer\Json\JsonML(<span style="color: #d0d0ff;">$dom</span>),
<span style="color: #6d9cbe;">JSON_PRETTY_PRINT</span>);
<span style="color: #cc7833;">echo </span><span style="color: #d0d0ff;">$json</span>;
<span style="color: #cc7833;">echo </span><span style="color: #a5c261;">"</span><span style="color: #519f50;">\n\n</span><span style="color: #a5c261;">JsonML -> XML</span><span style="color: #519f50;">\n\n</span><span style="color: #a5c261;">"</span>;
<span style="color: #cc7833;">echo </span><span style="color: #ffc66d;">FluentDOM</span>(
<span style="color: #d0d0ff;">$json</span>, <span style="color: #a5c261;">'application/jsonml+json'</span>)<span style="color: #cc7833;">-></span><span style="color: #ffc66d;">formatOutput</span>();</pre>
<br />
<h2>
The Release </h2>
<ul>
<li><a href="https://github.com/FluentDOM/FluentDOM/releases/tag/5.1.0" rel="nofollow">Github</a> </li>
<li><a href="https://packagist.org/packages/fluentdom/fluentdom" rel="nofollow">Packagist</a></li>
</ul>
ThWhttp://www.blogger.com/profile/12009821760062911474noreply@blogger.com0tag:blogger.com,1999:blog-3653745335475875863.post-24886855716965632062014-08-17T14:59:00.000+02:002014-08-18T13:52:19.571+02:00FluentDOM + HTML5HTML 5 is not directly supported by PHPs DOM extension. That means FluentDOM can not understand it, too. But here is a solution. <a href="https://github.com/Masterminds/html5-php" rel="nofollow">HTML5-PHP</a> is library that can parse HTML5 into a DOM document.<br />
<br />
Both libraries use Composer:<br />
<pre style="background-color: #2b2b2b; color: #e6e1dc; font-family: 'Courier New'; font-size: 12pt;"><span style="color: #d0d0ff;">"require"</span><span style="color: #cc7833;">: </span>{
<span style="color: #d0d0ff;">"fluentdom/fluentdom"</span><span style="color: #cc7833;">: </span><span style="color: #a5c261;">"5.*"</span>,
<span style="color: #d0d0ff;">"masterminds/html5"</span><span style="color: #cc7833;">: </span><span style="color: #a5c261;">"2.*"</span>
}</pre>
<br />
Read HTML5 into FluentDOM:<br />
<pre style="background-color: #2b2b2b; color: #e6e1dc; font-family: 'Courier New'; font-size: 12pt;"><span style="background-color: #472c47; color: #d0d0ff;">$html5</span><span style="color: #d0d0ff;"> </span><span style="color: #cc7833;">= new </span>Masterminds\HTML5();
<span style="color: #d0d0ff;">$fd </span><span style="color: #cc7833;">= </span><span style="color: #ffc66d;">FluentDOM</span>(<span style="background-color: #3c3c57; color: #d0d0ff;">$html5</span><span style="color: #cc7833;">-></span><span style="color: #ffc66d;">loadHTML</span>(<span style="color: #d0d0ff;">$html</span>));</pre>
<br />
Or write it:<br />
<pre style="background-color: #2b2b2b; color: #e6e1dc; font-family: 'Courier New'; font-size: 12pt;"><span style="color: #cc7833;">echo </span><span style="color: #d0d0ff;">$html5</span><span style="color: #cc7833;">-></span><span style="color: #ffc66d;">saveHTML</span>(<span style="color: #d0d0ff;">$fd</span><span style="color: #cc7833;">-></span><span style="color: #d0d0ff;">document</span>);</pre>
<br />
HTML5-PHP puts the elements into the XHTML namespace. To use XPath expressions, you will need to register a prefix for it:<br />
<pre style="background-color: #2b2b2b; color: #e6e1dc; font-family: 'Courier New'; font-size: 12pt;"><span style="background-color: #472c47; color: #d0d0ff;">$html5</span><span style="color: #d0d0ff;"> </span><span style="color: #cc7833;">= new </span>Masterminds\HTML5();
<span style="color: #d0d0ff;">$fd </span><span style="color: #cc7833;">= </span><span style="color: #ffc66d;">FluentDOM</span>(<span style="background-color: #3c3c57; color: #d0d0ff;">$html5</span><span style="color: #cc7833;">-></span><span style="color: #ffc66d;">loadHTML</span>(<span style="color: #d0d0ff;">$html</span>));
<span style="color: #d0d0ff;">$fd</span><span style="color: #cc7833;">-></span><span style="color: #ffc66d;">registerNamespace</span>(
<span style="color: #a5c261;">'xhtml'</span>, <span style="color: #a5c261;">'http://www.w3.org/1999/xhtml'</span>
);
<span style="color: #cc7833;">echo </span><span style="color: #d0d0ff;">$fd</span><span style="color: #cc7833;">-></span><span style="color: #ffc66d;">find</span>(<span style="color: #a5c261;">'//xhtml:p'</span>)<span style="color: #cc7833;">-></span><span style="color: #ffc66d;">text</span>();</pre>
ThWhttp://www.blogger.com/profile/12009821760062911474noreply@blogger.com0tag:blogger.com,1999:blog-3653745335475875863.post-73732265553321710272014-08-09T00:10:00.000+02:002014-08-09T00:10:02.217+02:00Xpath 1.0 - Quoting StringsStrings in Xpath 1.0 can be enclosed in single or double quotes. The following expressions are equivalent.<br />
<br />
<code>
//div[@id = 'foo']<br />
//div[@id = "foo"]<br />
</code>
<br />
<br />
This is nice because you can use the variant that requires less or none escaping. I prefer single quotes for PHP because they need less escaping (only single quote and backslash). I usually end up with something like this:<br />
<br />
<code>$xpath->evaluate('//div[@id = "foo"]');</code><br />
<br />
However a problem comes up if the value is dynamic. <br />
<br />
<code>$xpath->evaluate('//div[@id = "'.$_GET['foo'].'"]');</code><br />
<br />
If <tt>$_GET['foo']</tt> contains a double quote, it will break the expression. It is compare able to an SQL-Injection and should be avoided, don't you think?<br />
<br />
The <a href="http://www.w3.org/TR/xpath/#exprlex" rel="nofollow" target="_blank">Xpath 1.0 specification for a literal</a> is:<br />
<br />
<table><tbody>
<tr valign="baseline"><td>Literal</td><td> ::= </td><td>'"' [^"]* '"'</td>
</tr>
<tr valign="baseline">
<td style="text-align: center;"><br /></td><td><br /></td><td>| "'" [^']* "'"</td></tr>
</tbody></table>
<br />
It disallows the use of the enclosing quote in the literal itself, here is no way to escape it.<br />
<br />
<i>Hint: This is different in Xpath 2.0</i><i>. Y</i><i>ou can <a href="http://www.w3.org/TR/xpath20/#id-literals" target="_blank">duplicate the quotes</a> to escape them.</i><br />
<br />
<h3>
Deciding Which Quote To Use</h3>
The first and obvious step is to check the value for quotes and use the one that it does not contain:<br />
<br />
<code>
function quote($value) {<br />
$char = strpos($value, '"') === FALSE ? '"' : "'";<br />
return $char.$value.$char;<br />
}</code><br />
<br />
But a value could contain both kind of quotes. This would still break the expression.<br />
<h3>
Divide And Conquer</h3>
If is not possible to quote the whole value because it contains both kind of quotes you need to divide it into parts that can be quoted. You can then use the Xpath function concat() to rebuild the orignal value again:<br />
<br />
<code>//div[. = concat("Singe Quote: '", 'Double Quote: "')]</code><br />
<br />
Matching text structures is the domain of regular expression. So lets use them:<br />
<br />
<code>preg_match_all('("[^\']*|[^"]+)', 'Double Quote ", Single Quote \'', $matches);<br />
var_dump($matches);</code><br />
<br />
Output:<br />
<br />
<pre>array(1) {
[0]=>
array(3) {
[0]=>
string(13) "Double Quote "
[1]=>
string(16) "", Single Quote "
[2]=>
string(1) "'"
}
}</pre>
<br />
The pattern matches any string that start with a double quote and contains no single quote or any string that does not contain any double quote.<br />
<br />
All that is left is quoting the parts and join them back together:<br />
<br />
<code>
foreach ($matches[0] as $part) {<br />
$quoteChar = (substr($part, 0, 1) == '"') ? "'" : '"';<br />
$result .= ", ".$quoteChar.$part.$quoteChar;<br />
}<br />
return 'concat('.substr($result, 2).')';<br />
</code><br />
<h3>
Put Together</h3>
It does not make sense to create the function call for a single argument. So the check is still needed:<br />
<br />
<ol>
<li>If the value contains no single quote, use single quotes</li>
<li>If the value contains no double quote, use the double quotes</li>
<li>Otherwise divide the string and use concat()</li>
</ol>
<br />
A complete implementation can be found in <a href="https://github.com/FluentDOM/FluentDOM/blob/master/src/FluentDOM/Xpath.php#L127" target="_blank">FluentDOM\Xpath::quote()</a>.ThWhttp://www.blogger.com/profile/12009821760062911474noreply@blogger.com0tag:blogger.com,1999:blog-3653745335475875863.post-38746211937948127992014-08-06T17:10:00.000+02:002014-08-06T17:10:16.624+02:00FluentDOM 5 + XML Namespaces<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-_zPhs2dM36E/U-H5_igH4YI/AAAAAAAADbU/IP1QUPEUUYs/s1600/fluentdom_logo_large.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="http://4.bp.blogspot.com/-_zPhs2dM36E/U-H5_igH4YI/AAAAAAAADbU/IP1QUPEUUYs/s1600/fluentdom_logo_large.png" height="155" width="200" /></a></div>
FluentDOM 5 allows to register namespaces on the DOM document class. These are not the namespaces of a loaded document, but a definition of namespaces for your programming logic.<br />
<h3>
Namespaces In An XML File</h3>
People mistake the namespace prefixes for the namespaces often. The namespace definitions are the xmlns:* Attributes. Let's take a really simple example:<br />
<br />
<code><atom:feed xmlns:atom="http://www.w3.org/2005/Atom"/></code><br />
<br />
<span class="s">This is a feed element in the Atom namespace. The actual namespace is <b>"</b></span><span class="s"><b>http://www.w3.org/2005/Atom"</b>. Because this would be difficult to read and write the prefix/alias 'atom' is defined for the namespace. </span><br />
<br />
<span class="s">You can read the element name as<b> '{http://www.w3.org/2005/Atom}:feed'</b></span><br />
<br />
<span class="s">It is possible to define a default namespace for elements in an XML. </span><br />
<br />
<span class="s"><code><feed xmlns="http://www.w3.org/2005/Atom"/></code> </span><br />
<br />
This should still be interpreted as <b><span class="s">'{</span></b><span class="s"><b>http://www.w3.org/2005/Atom}:feed'</b>. The namespace prefix in the source document is not relevant. You need a way to match the namespace itself and not the alias.</span><br />
<br />
<i>Hint 1: Namespace prefixes can be redefined on any element node in the document. </i><br />
<br />
<i><span class="s">Hint 2: Attributes always need a prefix to use a namespace. Any attribute without a prefix is in the "none/empty" namespace. </span></i><br />
<h3>
<span class="s">Namespaces in XPath</span></h3>
<span class="s">An XPath expresssion that could match the Atom feed element would need to use a prefix and needs a way to resolve that prefix into the actual namespace. </span><br />
<br />
A PHP Example:<br />
<br />
<code>$xpath = new DOMXpath($document);<br />$xpath->registerNamespace('a', 'http://www.w3.org/2005/Atom');<br />$nodes = $xpath->evaluate('/a:feed');</code><br />
<br />
In PHP the DOMXpath class provides the evaluate() method and the namespace resolver. You register your own namespace prefixes.<b> '/a:feed' </b>gets resolved to<b> '/{http://www.w3.org/2005/Atom}:feed'</b>. The prefixes in the document and the expression can be different, but the resolved namespace has to be the same.<br />
<br />
A JavaScript example:<br />
<br />
<code>
var xmlns = {<br />
namespaces : {<br />
'a' : 'http://www.w3.org/2005/Atom'<br />
},<br />
lookupNamespaceURI : function(prefix) {<br />
return this.namespaces[prefix] || null;<br />
}<br />
};<br />
var nodes = xmlDocument.evaluate('/a:feed', xmlDocument, xmlns, XPathResult.ANY_TYPE, null); </code><br />
<br />
In JavaScript the document object provides the evaluate() method and the namespace resolver is an argument for it. This will work in most of the current browsers, but not IE. <br />
<h3>
Namespaces in FluentDOM</h3>
In FluentDOM 5 the document and the element classes both have an evaluate() method. To define your namespace mapping you register the namespace on the document object:<br />
<br />
<code>$document->registerNamespace('a', 'http://www.w3.org/2005/Atom')<br />
$nodes = $document->evaluate('/a:feed');</code>
<br />
<br />
Now because the document has a way to resolve namespaces, it can do this for other methods, too. Basically for all methods a have a namespace aware variant, like 'createElement()' and 'createElementNS()' or 'setAttribute()' and 'setAttributeNS()'. If FluentDOM can resolve a tagname it will call the namespace aware variant of the method.<br />
<br />
<code>
// Standard DOM<br />
$feed = $document->createElementNS(<br />
'http://www.w3.org/2005/Atom', 'atom:feed'<br />
);<br />
// FluentDOM<br />
$document->registerNamespace('atom', 'http://www.w3.org/2005/Atom');<br />
$feed = $document->createElement('atom:feed');</code><br />
<h3>
Default Namespace </h3>
FluentDOM adds the concept of a default element namespace, too.<br />
<br />
<code>
// Standard DOM<br />
$feed = $document->createElementNS(<br />
'http://www.w3.org/2005/Atom', 'feed'<br />
);<br />
// FluentDOM<br />
$document->registerNamespace('#default', 'http://www.w3.org/2005/Atom');<br />
$feed = $document->createElement('feed');</code><br />
<h2>
appendElement()</h2>
<br />
FluentDOM\Document and FluentDOM\Element implement an appendElement() method. It is a shortcut for serveral methods. It creates the element, adds attributes and a text node and appends it to the parent node. Together with the namespace handling, creating an XML document becomes a lot simpler. You can find an <a href="https://github.com/FluentDOM/FluentDOM/wiki/Creating-XML-with-Namespaces-%28Atom%29" target="_blank">example in the wiki</a>.<br />
<br />
<br />ThWhttp://www.blogger.com/profile/12009821760062911474noreply@blogger.com0tag:blogger.com,1999:blog-3653745335475875863.post-27969977116318769502014-08-01T14:58:00.000+02:002014-08-06T14:34:05.164+02:00FluentDOM 5FluentDOM 5.0.0 is now released. <br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-_zPhs2dM36E/U-H5_igH4YI/AAAAAAAADbU/IP1QUPEUUYs/s1600/fluentdom_logo_large.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="http://4.bp.blogspot.com/-_zPhs2dM36E/U-H5_igH4YI/AAAAAAAADbU/IP1QUPEUUYs/s1600/fluentdom_logo_large.png" height="155" width="200" /></a></div>
Up to version 4.1, <a href="http://fluentdom.org/">FluentDOM</a> was an implementation of the jQuery Traversing and Manipulation APIs in PHP. <a href="https://github.com/FluentDOM/FluentDOM/releases/tag/5.0.0">Version 5</a> is a complete rewrite and adds a secondary focus. FluentDOM now provides extended variants of PHPs DOM classes, too. This allows workarounds for bugs, syntax sugar and shortcuts. <br />
<h2>
Bugs</h2>
<h3>
<a href="https://bugs.php.net/bug.php?id=39521" rel="nofollow" target="_blank">#39521</a> </h3>
The second argument to DOMDocument::createElement() it breaks if it contains an entity. FluentDOM avoids that by creating and appending a text node. Additionally it adds a third argument to provide attributes.<br />
<h3>
<a href="https://bugs.php.net/bug.php?id=55700" rel="nofollow" target="_blank">#55700</a></h3>
By default PHP registers the namespace definitions of the current context. This uses the namespace prefixes as identifiers, but they are not. They are allowed to change (even on different elements in the same document). You should always register your own prefixes so you do not depend on the prefixes in the document - they are just not relevant. So the automatic registration costs performance without a real gain in the best case. In the worst case it overrides a your namespace registration and you can not fetch the data you want.<br />
<br />
FluentDOM adds a property to change this behavior. The automatic namespace registration is disabled by default and can be activated using the property or the third argument for evaluate()/query().<br />
<h2>
Syntax Sugar</h2>
<h3>
Cast To String</h3>
<br />
Most of the nodes can be cast to string. for example the following will return the whole text content of an document.<br />
<br />
<code>echo $dom->documentElement;</code><br />
<h3>
Iterator For Child Nodes </h3>
<br />
FluentDOM\Element is iterate-able. Using foreach() on it will iterate over the child nodes. The Iterator is a RecursiveIterator, too.<br />
<h3>
ArrayAccess</h3>
FluentDOM\Element allows array syntax. A numeric key like $element[1] will access the child node. An qualified name string like $element['href'] will access the attribute.<br />
<h3>
Namespaces </h3>
FluentDOM\Document allows to register namespaces on the document. If methods like setAttribute() or createElement() recognize a colon in the tag name, they will resolve the namespace prefix an call their namespace aware variant.<br />
<h2>
Shortcuts</h2>
FluentDOM\Document::createElement() allows to provided content and attributes. FluentDOM\Element::appendElement() allows to create and append an element with a single call.<br />
<br />
Other methods of the FluentDOM\Element class are variants of the document variants using the element node as default context. <br />
<h2>
Backwards Compatbility</h2>
FluentDOM 5 is mostly backwards compatible. The FluentDOM() function still exists and the returned FluentDOM\Query instance has the same jQuery like API. Only the loaders where changed. <br />
<h2>
CSS Selectors</h2>
The FluentDOM\Query class allows to set an callback function that is used to convert the provided selectors to xpath expressions. FluentDOM::QueryCss() returns a FluentDOM\Query instance that supports CSS selectors. You will need to have Carica PhpCss or Symfony CSS-Selector installed in you project.<br />
<br />
<br />
<br />ThWhttp://www.blogger.com/profile/12009821760062911474noreply@blogger.com0tag:blogger.com,1999:blog-3653745335475875863.post-28712530291257431512013-11-17T14:55:00.000+01:002013-11-17T14:55:15.232+01:00Building A Sensor Phalanx With PHPSensors are fun. They report from the physical world into the digital. But getting the signal into php is only the first part, you will have to get them out again. This post shows how to get data from analog sensors pushed to the browser. It uses Carica Chip, if you haven't read my <a href="http://www.a-basketful-of-papayas.net/2013/11/carica-chip-101-controlling-led-with-php.html">previous blog post</a> you should do it first.<br />
<br />
Carica Io implements its own event loop, but if it finds <a href="http://reactphp.org/" rel="nofollow">ReactPHP</a> installed, it will base it on this implementation, making it compatible and allowing to use the libraries. One of the libraries for ReactPHP is <a href="http://socketo.me/" rel="nofollow">Ratchet</a>, a websocket server implementation.<br />
<br />
In this example two servers are used. An http server for file delivery (html and javascript for the UI) and a websocket server that pushes the sensor values to the browser. <a href="http://smoothiecharts.org/" rel="nofollow">Smootie Charts</a> displays the data in the browser as a running line chart, not unlike an <span dir="auto">oscilloscope.</span> <br />
<h2>
Source</h2>
The latest source for this example can be found on Bitbucket:<br />
<a href="https://bitbucket.org/ThomasWeinert/carica-sensor-phalanx" rel="nofollow">https://bitbucket.org/ThomasWeinert/carica-sensor-phalanx </a>
<br />
<h2>
Project Setup</h2>
Clone Carica Chip Skeleton to create a new project and add Ratchet to the dependencies.<br />
<br />
<code>
composer create-project carica/chip-skeleton SensorPhalanx \<br /> --stability=dev <br /><br />cd SensorPhalanx <br /><br />composer require cboden/ratchet<br /><br /> copy dist.configuration.php configuration.php </code>
<br />
Edit configure.php to match your hardware setup.<br />
<h2>
The HTTP Server</h2>
In the LED example, the http server had only one route to deliver the html file. Now we need to deliver the javascript, too. An additional route allows to deliver static files from a subdirectory.<br />
<br />
<code>
$route = new Http\Route();<br />$route->match('/', new Http\Route\File('index.html'));<br />$route->startsWith('/files', new Http\Route\Directory(__DIR__));<br />$httpServer = new Http\Server($route);<br />$httpServer->listen(8080); </code><br />
<h2>
The Sensor Phalanx</h2>
The Sensor Phalanx object encapsulates the Ratchet websocket server and provides the callbacks for it. It needs an array of sensors. Port 8080 is already used for the http server, so it should listen on port 8081. An interval triggers the update of the clients.<br />
<br />
<code>
// create the phalanx with some sensors<br />include(__DIR__.'/class/SensorPhalanx.php');<br />$phalanx = new Carica\SensorPhalanx(<br /> [<br /> 'lightsensor' => new Chip\Sensor\Analog($board->pins[15]),<br /> 'potentiometer' => new Chip\Sensor\Analog($board->pins[16])<br /> ]<br />);<br />// tell the websocket server to listen<br />$phalanx->listen(8081);<br /><br />// update clients connected to the phalanx with the current sensor data<br />$loop->setInterval(<br /> function () use ($phalanx) {<br /> $phalanx->update();<br /> },<br /> 200<br />); </code><br />
<h2>
The SensorPhalanx Class</h2>
The SensorPhalanx class needs to implement two interfaces. Carica\Io\Event\HasLoop defines access to the Carica Io event loop. It can be implemented using the trait Carica\Io\Event\Loop\Aggregation.<br />
<br />
Ratchet\MessageComponentInterface defines the callback methods for the websocket server.<br />
<ul>
<li>onOpen() - add a client</li>
<li>onClose() - remove a client</li>
<li>onMessage() - handle data recieved from the browser</li>
<li>onError() - log/handle errors</li>
</ul>
<code>
public function onOpen(Ratchet\ConnectionInterface $connection) {<br /> $this->_clients[spl_object_hash($connection)] = $connection;<br />}<br /><br />public function onClose(Ratchet\ConnectionInterface $connection) {<br /> unset($this->_clients[spl_object_hash($connection)]);<br />}<br /><br />public function onMessage(Ratchet\ConnectionInterface $connection, $message) {<br />}<br /><br />public function onerror(Ratchet\ConnectionInterface $connection, \Exception $e) {<br /> echo "Error: ", $e->getMessage(), "\n";<br /> $connection->close();<br />} </code><br />
<h3>
Create And Listen </h3>
The constructor of the class just validates the sensor objects, and stores them. Listen creates the needed subobjects. Unlike the examples on the Ratchet website, the factory methods can not be used. They would create a new event loop. The script already has one, used for the Arduino board and the http server. The $this->loop() method returns the Carica Io event loop and in a second step allows access to the actual ReactPHP event loop.<br />
<br />
<code>
public function __construct(array $sensors) {<br /> foreach ($sensors as $index => $sensor) {<br /> if ($sensor instanceOf Chip\Sensor\Analog) {<br /> $this->_sensors[$index] = $sensor;<br /> }<br /> }<br />}<br /><br />public function listen($port = 8081) {<br /> $socket = new \React\Socket\Server($this->loop()->loop());<br /> $socket->listen($port);<br /> $this->_server = new Ratchet\Server\IoServer(<br /> new Ratchet\Http\HttpServer(<br /> new Ratchet\WebSocket\WsServer(<br /> $this<br /> )<br /> ),<br /> $socket,<br /> $this->loop()->loop()<br /> );<br />} </code><br />
<h3>
Update</h3>
Update collects the data from all sensors into an array, encodes it to json and sends it to all connected clients.<br />
<br />
<code>
public function update($log = TRUE) {<br /> $values = ['type' => 'sensors'];<br /> foreach ($this->_sensors as $index => $sensor) {<br /> $values['sensors'][$index] = $sensor->get();<br /> }<br /> $json = json_encode($values);<br /> if ($log) {<br /> echo $json, "\n";<br /> }<br /> foreach ($this->_clients as $client) {<br /> $client->send($json);<br /> }<br />}</code><br />
<h2>
User Interface</h2>
The html file uses Smoothie Charts. It defines some CSS and a canvas for the chart. The chart is created and connected to the canvas. A list of colors provides a different color for each line.<br />
<br />
The websocket server connects and gets a callback. If a message is received it loops over the sensor values. If a line for the index already exists the value is appended. For an unknown index a new line is created and the assigned color is removed from the internal list.<br />
<br />
<code>
var smoothie = new SmoothieChart(<br /> {<br /> maxValue : 1,<br /> minValue: 0<br /> }<br />);<br />smoothie.streamTo(document.getElementById('monitorCanvas'), 250);<br /><br />var colors = ['#00FF00', '#FF0000', '#0000FF', '#FFFF00', '#FF00FF', '#00FFFF'];<br />var lines = [];<br /><br />websocket = new WebSocket('ws://localhost:8081/');<br />websocket.onmessage = function(event) {<br /> var data = JSON.parse(event.data);<br /> if (data && data.type == 'sensors') {<br /> for (index in data.sensors) {<br /> if (typeof lines[index] == 'undefined') {<br /> var color = colors.shift();<br /> if (!color) {<br /> color = '#FFFFFF';<br /> }<br /> lines[index] = new TimeSeries();<br /> smoothie.addTimeSeries(<br /> lines[index],<br /> {<br /> lineWidth: 2,<br /> strokeStyle: color<br /> }<br /> );<br /> }<br /> lines[index].append(new Date().getTime(), data.sensors[index]);<br /> }<br /> }<br />}; </code><br />
<h2>
In Action </h2>
<object class="BLOGGER-youtube-video" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" data-thumbnail-src="http://i1.ytimg.com/vi/EUl_RLTBKTk/0.jpg" height="266" width="320"><param name="movie" value="http://www.youtube.com/v/EUl_RLTBKTk?version=3&f=user_uploads&c=google-webdrive-0&app=youtube_gdata" /><param name="bgcolor" value="#FFFFFF" /><param name="allowFullScreen" value="true" /><embed width="320" height="266" src="http://www.youtube.com/v/EUl_RLTBKTk?version=3&f=user_uploads&c=google-webdrive-0&app=youtube_gdata" type="application/x-shockwave-flash" allowfullscreen="true"></embed></object>
ThWhttp://www.blogger.com/profile/12009821760062911474noreply@blogger.com0tag:blogger.com,1999:blog-3653745335475875863.post-64182173639284114442013-11-04T21:36:00.000+01:002014-02-28T22:37:46.905+01:00Carica Chip 101 - Controlling An LED With PHPSome time ago, in this<a href="http://www.a-basketful-of-papayas.net/2013/06/basics-of-using-arduino-from-php.html"> blog post</a>, I explained the basic stuff about Arduino, Firmata and PHP. Now it is time for the next step. Carica Io and Carica Firmata have grown and got a third layer called Carica Chip.<br />
<ol>
<li><a href="https://github.com/ThomasWeinert/carica-io">Carica Io</a> - Non-Blocking I/O for PHP</li>
<li><a href="https://github.com/ThomasWeinert/carica-firmata">Carica Firmata</a> - An implementation of the <a href="http://firmata.org/">Firmata</a> protocol</li>
<li><a href="https://github.com/ThomasWeinert/carica-chip" rel="nofollow">Carica Chip</a> - PHP classes representing hardware devices</li>
</ol>
Carica Chip provides an easy way to control a device. So let's start with an "Interactive LED" example.
<br />
<h2>
First Step: Project Initialization</h2>
<a href="http://3.bp.blogspot.com/-85xmy18MqBI/UnfmbixK0CI/AAAAAAAADTQ/rOgIpxgx1sQ/s1600/composer-create-project-carica-chip.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" src="http://3.bp.blogspot.com/-85xmy18MqBI/UnfmbixK0CI/AAAAAAAADTQ/rOgIpxgx1sQ/s200/composer-create-project-carica-chip.png" height="154" width="200" /></a>
Carica Chip uses <a href="http://getcomposer.org/">Composer</a>. Make sure that it is installed and open a console. Go into your projects directory and execute the following line (it will create a new subdirectory "led"):
<br />
<br />
<pre style="clear: right;">composer create-project carica/chip-skeleton led \
--stability=dev</pre>
<h2>
Second Step: Create An HTML Interface</h2>
For the interface a simple html file is used. Just two links with an iframe set as the target. This is a basic version, some Javascript and CSS should be used to make it nicer and more usable.<br />
<br />
<pre><html>
<head>
<title>Led Switch</title>
</head>
<body>
<a href="./switch/on" target="iframe">
On
</a>
<a href="./switch/off" target="iframe">
Off
</a>
<iframe name="iframe" src="about:blank"></iframe>
</body>
</html></pre>
<br />
Store the html source as "index.html" in the project root.<br />
<h2>
Third Step: Create The PHP Server</h2>
This will be a step by step description, the complete file is below and on <a href="https://gist.github.com/ThomasWeinert/7308145#file-server-php">Gist</a>.<br />
<br />
The skeleton includes a bootstrap file that returns a board depending on your configuration. Copy "dist.configuration.php" to "configuration.php" and change it if needed.
<br />
<br />
Now open "server.php". At the top "bootstrap.php" is included to fetch a Firmata board. If the board is activated, it executes a callback. Carica Chip provides a "Led" class. For the example an instance is created. The constructor needs the pin the led is connected to. Most Arduinos have a led that is connected to pin #13 on board.
<br />
<pre>$led = new Chip\Led($board->pins[13]);</pre>
<br />
We need to deliver the html interface to the browser. Carica Io includes a http server and file routings. Add "use Carica\Io\Network\Http as Http;" to the namespace definition, create a new route and add a file delivery for "index.html".
<br />
<pre>$route = new Http\Route();
// Define html file delivery for /
$route->match(
'/',
new Http\Route\File(__DIR__.'/index.html')
);</pre>
<br />
A second route defines the switch actions. This is specific and not much source, so an anonymous function is used. For more extensive logic I suggest functors, objects that implement the magic method "__invoke()", like the file handler. The state parameter is fetched from the path. Depending on the parameter the led is switched on or off. A response is created and a string with the new state is used as content. If you don't return a response, a 404 would be send to the browser.<br />
<pre>$route->match(
'/switch/{state}',
function (Http\Request $request, array $parameters)
use ($led) {
$ledOn = ($parameters['state'] == 'on');
if ($ledOn) {
$led->on();
} else {
$led->off();
}
$response = $request->createResponse(
new Http\Response\Content\String(
$ledOn ? 'ON' : 'OFF',
'text/plain; charset=utf-8'
)
);
return $response;
}
);</pre>
<br />
Last the http server is created and started.<br />
<pre>$server = new Carica\Io\Network\Http\Server($route);
$server->listen(8080);</pre>
<h2>
Finished!</h2>
<a href="http://3.bp.blogspot.com/-Rt8fp8gjia4/Unf6Oq9Fh3I/AAAAAAAADTg/PWZUnS9_--g/s1600/carica-chip-led-running.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-Rt8fp8gjia4/Unf6Oq9Fh3I/AAAAAAAADTg/PWZUnS9_--g/s200/carica-chip-led-running.png" /></a>
That's all. You can now start the script on the command line and open the URL in a browser. Clicking the links will (de)activate the led.
<br />
<br />
<div style="clear: both;">
I posted the full source of "<a href="https://gist.github.com/ThomasWeinert/7308145#file-server-php">server.php</a>" including comments on Gist.</div>
<script src="https://gist.github.com/ThomasWeinert/7308145.js"></script>
ThWhttp://www.blogger.com/profile/12009821760062911474noreply@blogger.com0tag:blogger.com,1999:blog-3653745335475875863.post-57518579938613083432013-06-20T21:00:00.000+02:002014-02-28T22:39:37.273+01:00Basics: Using Arduino From PHP<h2>
Own Protocol Inside A PHP Page. </h2>
This is the version mostly described on the net. For example on Instructables. Basically you write an Arduino Scetch that listens and send plain text data on the serial port. A PHP Script triggered by a webserver request opens the port communicates with the Arduino board an outputs some HTML.<br />
<br />
The major downside of this approach is that many Arduino boards reset if the serial port is opened. So the startup and configuration has to be done any time the PHP scripts reads some data or triggers an action. Because the PHP script stops after the page request any data has to be stored in a database, cache or file.<br />
<h2>
Firmata</h2>
<a href="http://www.firmata.org/wiki/Main_Page" rel="nofollow">Firmata</a> is a generic sketch for Arduino boards that implements an MIDI based protocol. Here are client libraries for several languages. For example Johnny Five for Javascript (Node.js) implements abstraction not only for the communication but for hardware elements. For example you get an led object with blink() and fade() methods.<br />
<h3>
Firmata Inside A PHP Page. </h3>
Well, It is possible but you don't want to do that. Firmata has a little more complex startup process that takes about two seconds. To much to use it in a web request.<br />
<h3>
Firmata Over TCP</h3>
The official version of Firmata uses the serial port. But here are forks that can use TCP. You need an compatible network shield for your Arduino board and the version may be a little behind the offical one.<br />
<h2>
Non-Blocking I/O</h2>
Node.js uses an concept called Non-blocking I/O or Asynchronous I/O. Basically it means that you add actions to an event loop, trigger events from you objects that execute attached callbacks. Doesn't sound familiar? If you ever programmed some Javascript for your web pages, you used this concept. Each browser window has an event loop. Functions like "window.setTimeout()" add event to the loop. The browser fires event on the page elements that execute callbacks, if you attached them, too.<br />
<br />
So using this concept you describe what should happen if an events is triggered and a certain conditions are meet. In classic PHP you work with the current state. In Non-Blocking I/O the result has yet to be calculate at a future time.<br />
<br />
<i>Procedual Programming:</i><br />
<blockquote class="tr_bq">
If the result of action x <b>is</b> value y <b>now do</b> that.</blockquote>
<i>Non-Blocking I/O</i><br />
<blockquote class="tr_bq">
If the result of action x <b>will be</b> y you <b>will have to do</b> that.</blockquote>
<br />
Thinking like this is much better suited to hardware control, at the moment you are programming you do not now if somebody will ever press the button on your Arduino device or which values the sensor your connected will send.<br />
<h3>
Non-Blocking I/O in PHP</h3>
PHP has no default event loop implemented but here are several possibilities for it. First it whould be possible just using an while loop and usleep(). This would not be very efficient. Better is an combination of this with stream_select(). The "stream_select()" function is like a usleep() that monitors streams and return imminently if a stream is changed. It even says which stream is changed how, so it is possible to trigger the exact events for this stream.<br />
<br />
Event more efficient is using libevent. PHP has an extension for this event loop implementation. It is used inside the Chrome browser browser and Node.js, too.<br />
<h3>
Libraries For PHP</h3>
Here are libraries in PHP that implement event loops. <br />
<br />
<b>ReactPHP</b><br />
ReactPHP is the most mature project implementing Non-Blocking I/O for PHP. It a general purpose library and has additional components. You can read more about it on <a href="http://reactphp.org/" rel="nofollow">reactphp.org</a>.<br />
<br />
<b>Carica Io</b><br />
Caica Io is an implementation I created as a base for Carica Firmata, a client library for the Firmata protocol. It can used separately if you're going to implement your own protocol, too. Here is an <a href="https://bitbucket.org/ThomasWeinert/carica-io/src/58ca2ff80f96ae0b28090ceea73be18905802d3f/example/loop.php?at=master">basic example with timers</a>:<br />
<br />
<script src="https://bitbucket.org/ThomasWeinert/carica-io/src/58ca2ff80f96ae0b28090ceea73be18905802d3f/example/loop.php?embed=t"></script><br />
The event loop uses the API not unlike the Browser. The main difference to Javascript in the Browser or Node.js is that you have to run the event loop explicitly. <br />
<h2>
Carica Firmata</h2>
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; margin-left: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-l2JKJQXDGJY/UcQjTO6lHXI/AAAAAAAADMQ/V8J5RrZViWU/s1600/carica-firmata-test.png" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" src="http://2.bp.blogspot.com/-l2JKJQXDGJY/UcQjTO6lHXI/AAAAAAAADMQ/V8J5RrZViWU/s200/carica-firmata-test.png" height="93" width="200" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">The Firmata test application<br />
in Carica Firmata</td></tr>
</tbody></table>
Based on Carica Io, it allows you to create programs that control the Arduino board. They run without an webserver (like Apache HTTP, Nginx, ...). They can even contain an (simple) webserver to control the script from a browser. The php program is started from the command line and runs continuously until you stop it (or it crashes :-) ). The serial connection to the board is opened only once, no reset happens and the start up time is not an issue.<br />
<h3>
Blink </h3>
The "Hello World" for an Arduino is <a href="https://github.com/ThomasWeinert/carica-firmata/blob/master/example/blink.php">an blinking led</a>. Wait for the board to activate, and add an interval (repeated timer) that switches it on and off.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.youtube.com/embed/2DbDcx7RHbg?feature=player_embedded' frameborder='0'></iframe></div>
<script src="https://bitbucket.org/ThomasWeinert/carica-firmata/src/99fffc5241237b8aa52c804bb824fce21ab3fbaf/example/blink.php?embed=t"></script>
<br />
<h2>
Dimmer</h2>
Carica Firmata can read data, too. Simple attach an callback to the board. On the analog pin this could be a <a href="https://github.com/ThomasWeinert/carica-firmata/blob/master/example/dimmer.php">potentiometer dimming an led</a>. The examples uses an OOP api for the pins. The main difference to calling the methods on the board object directly is that values and the mode are only send if the are changed.
<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.youtube.com/embed/6DStDa3j3yw?feature=player_embedded' frameborder='0'></iframe></div>
<script src="https://bitbucket.org/ThomasWeinert/carica-firmata/src/99fffc5241237b8aa52c804bb824fce21ab3fbaf/example/dimmer.php?embed=t"></script>
<br />
<h2>
On Windows</h2>
On Windows the serial port is seriously broken at the moment. It can not work non-blocking. Any reading action will block your script until it recieves data. One solution is Serpoxy, a little program that maps the serial port to tcp.
<br />
<h2>
Links</h2>
<ul>
<li><a href="https://github.com/ThomasWeinert/ThomasWeinert/carica-io">Carica Io</a></li>
<li><a href="https://github.com/ThomasWeinert/carica-firmata">Carica Firmata</a></li>
<li><a href="http://www.firmata.org/wiki/Main_Page" rel="nofollow">Firmata</a></li>
<li><a href="http://reactphp.org/" rel="nofollow">ReactPHP</a></li>
</ul>
ThWhttp://www.blogger.com/profile/12009821760062911474noreply@blogger.com0tag:blogger.com,1999:blog-3653745335475875863.post-52150662262812179822012-07-31T21:21:00.000+02:002012-07-31T21:25:23.351+02:00What Iterators Can Do For YouBasically Iterators provide a list interface for an object. Like all interfaces they are a contract how something can be used. If you use an interface it is not relevant how it is implemented - the implementation logic is encapsulated.<br />
<br />
It is of course relevant on the integration level. A bad implementation can impact the performance of you application. Even an good implementation may need special resources (like a database). But all this does not impact how you use it. Your code using the object with the Iterator interface stays the same.<br />
<br />
Let's start with a simple example that outputs a list.<br />
<br />
<code>$elements = array(<br />
'line one', 'line two'<br />
);<br />
<br />
foreach ($lines as $key => $value) {<br />
echo $key.': '.$value."\n";<br />
}<br />
</code><br />
<br />
If we transfer this into an object it would like that:<br />
<br />
<code>class MyProjectLineOutput {<br />
private $_lines = NULL;<br />
<br />
public function __construct($lines) {<br />
$this->_lines = $lines;<br />
}<br />
<br />
public function __invoke() {<br />
foreach ($this->_lines as $key => $value) {<br />
echo $key.': '.$value."\n";<br />
}<br />
}<br />
}<br />
<br />
$output = new MyProjectLineOutput(<br />
array(<br />
'line one', 'line two'<br />
)<br />
);<br />
$output();<br />
</code><br />
<br />
On the first glance that looks like a lot more work but it isn't. The code includes two tasks. Get the lines and output them. The class encapsulates the output task and makes it reusable. In this simple example that may look superfluous but think a little larger. Like output an select-field or csv.<br />
<h2>
Encapsulate file()</h2>
<br />
Still we haven't used an Iterator, but just encapsulated the output. PHP provides several default iterators and one of them is the ArrayIterator.<br />
<br />
<code>$output = new MyProjectLineOutput(<br />
new ArrayIterator(<br />
array(<br />
'line one', 'line two'<br />
)<br />
)<br />
);<br />
$output();</code><br />
<br />
The <a href="http://php.net/manual/en/class.arrayiterator.php" rel="nofollow">ArrayIterator</a> just takes an array and makes it an Iterator. It is mostly used to implement another interface - <a href="http://php.net/manual/en/class.iteratoraggregate.php" rel="nofollow">IteratorAggregate</a>. Both the "Iterator" and the "IteratorAggregate" interfaces inherit from a common ancestor named "Traversable". You can not implement "Traversable" directly but use it to validate if an object is traversable or in other words can be used with foreach.<br />
<br />
Now let's load the lines from a file.<br />
<br />
<code>class MyProjectFile implements IteratorAggregate {<br />
private $_file;<br />
<br />
public function __construct($file) {<br />
$this->_file = $file;<br />
}<br />
<br />
public function getIterator() {<br />
return new ArrayIterator(file($this->_file));<br />
}<br />
}<br />
<br />
$output = new MyProjectLineOutput(<br />
new MyProjectFile('sample.txt')<br />
);<br />
$output();<br />
</code><br />
<br />
The main difference between using file directly to this is that the file() is accessed later in the process. The foreach() inside the MyProjectLineOutput::output() method calls MyProjectFile::getIterator(). Until then we can pass the instance of MyProjectFile around without loading the file into memory. Unlike a direct call to file() we don't pass the concrete data around but an information how it can be obtained.<br />
<h2>
Iterating A Text File</h2>
<br />
Implementing Iterator we can make sure that only a part of the file needs to be loaded.<br />
<br />
<code>class MyProjectFileUnbuffered implements Iterator {<br />
private $_file;<br />
private $_handle = NULL;<br />
private $_key = -1;<br />
private $_current = NULL;<br />
<br />
public function __construct($file) {<br />
$this->_file = $file;<br />
}<br />
<br />
public function __destruct() {<br />
if (is_resource($this->_handle)) { <br />
fclose($this->_handle);<br />
}<br />
}<br />
<br />
public function rewind() {<br />
if (!is_resource($this->_handle)) { <br />
$this->_handle = fopen($this->_file, 'r');<br />
} else {<br />
fseek($this->_handle, 0);<br />
}<br />
$this->_key = -1;<br />
$this->next();<br />
}<br />
<br />
public function next() {<br />
if ($this->_key > 0 or $this->_current !== FALSE) {<br />
$this->_current = fgets($this->_handle);<br />
$this->_key++;<br />
}<br />
}<br />
<br />
public function key() {<br />
return $this->_key;<br />
}<br />
<br />
public function current() {<br />
return $this->_current;<br />
}<br />
<br />
public function valid() {<br />
return $this->_current !== FALSE;<br />
}<br />
}</code><br />
<br />
This is more source then implementing it directly. Mostly because of the class and function declarations. But you have to write this only once. And it can be improved or replaced without affecting the usage.<br />
<h2>
Map Elements</h2>
Using iterators you can encapsulate mapping actions, like using array_map() on an array but only for elements that are read. Let's say you need to chop all trailing whitespaces from the lines.<br />
<h3>
Step One: Map Iterator:</h3>
<br />
<code>class MyProjectMapIterator implements OuterIterator {<br />
<br />
private $_innerIterator = NULL;<br />
private $_callback = NULL;<br />
<br />
public function __construct(Iterator $innerIterator, $callback) {<br />
$this->_innerIterator = $innerIterator;<br />
$this->_callback = $callback;<br />
}<br />
<br />
public function map($current, $key) {<br />
return call_user_func($this->_callback, $current, $key);<br />
}<br />
<br />
public function getInnerIterator() {<br />
return $this->_innerIterator;<br />
}<br />
<br />
public function rewind() {<br />
$this->getInnerIterator()->rewind();<br />
}<br />
<br />
public function next() {<br />
$this->getInnerIterator()->next();<br />
}<br />
<br />
public function key() {<br />
return $this->getInnerIterator()->key();<br />
}<br />
<br />
public function current() {<br />
return $this->map(<br />
$this->getInnerIterator()->current(),<br />
$this->getInnerIterator()->key()<br />
);<br />
}<br />
<br />
public function valid() {<br />
return $this->getInnerIterator()->valid();<br />
}<br />
}</code><br />
<br />
<a href="http://php.net/manual/en/class.outeriterator.php" rel="nofollow">OuterIterator</a> is an interface for iterators that wraps other iterators, it is defined in the <a href="http://php.net/manual/en/book.spl.php" rel="nofollow">SPL</a>. It extends the Iterator interface. The MapIterator is an iterator of that kind, so it is cleaner to implement it that way.<br />
<h3>
Step Two: Using The Map Iterator:</h3>
Using the map iterator is not unlike using array_map.<br />
<br />
<code>$output = new MyProjectLineOutput(<br />
new MyProjectMapIterator(<br />
new MyProjectFile('sample.txt'),<br />
function($current, $key) {<br />
return chop($current);<br />
}<br />
)<br />
);<br />
$output();</code><br />
<br />
The file() function has an option to do this. But it is limited to exactly this task. With MapIterator you get a lot more flexibility. You could even extend the MapIterator to get reusable mappings.<br />
<h3>
Step Three: Extending the Map Iterator </h3>
<code>class MyProjectMapIteratorUpper extends MyProjectMapIterator {<br />
<br />
public function __construct(Iterator $innerIterator) {<br />
parent::__construct(<br />
$innerIterator,<br />
function($current, $key) {<br />
return strToUpper($current);<br />
}<br />
);<br />
}<br />
}</code><br />
<h2>
Filter Elements:</h2>
Here is another option for file(), that skips empty lines. This would be filter task and here is already an superclass for that in <a href="http://http//php.net/manual/en/book.spl.php" rel="nofollow">SPL</a>.<br />
<br />
<code>class MyProjectFilterIteratorSkipEmptyLines extends FilterIterator {<br />
<br />
public function accept() {<br />
return trim($this->getInnerIterator()->current()) !== '';<br />
}<br />
}</code><br />
<h2>
Conclusion</h2>
Iterators are not about writing less code at one time. But they help you to write source that is encapsulated, easy to test and reusable. Because of this over time you will end up with less code.<br />
<br />ThWhttp://www.blogger.com/profile/12009821760062911474noreply@blogger.com3tag:blogger.com,1999:blog-3653745335475875863.post-80422465695327526542012-07-30T23:14:00.000+02:002012-07-31T00:55:42.731+02:00Using The PHP 5.4 Webserver On Windows<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-rMcALNnQ4OE/UBbtg8MvtvI/AAAAAAAAC9I/MXFyvnlGWKU/s1600/00-console.png" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" height="100" src="http://2.bp.blogspot.com/-rMcALNnQ4OE/UBbtg8MvtvI/AAAAAAAAC9I/MXFyvnlGWKU/s200/00-console.png" width="200" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Starting a server from the command line</td></tr>
</tbody></table>
PHP 5.4 has an built-in webserver. For local development it is not necessary to install Apache Httpd or another webserver anymore. You can just start an server from the command line.<br />
<br />
Change the directory to your project directory. The argument -S starts the webserver. You need to specify a host and a port. All requests are shown in the console window.<br />
<h2>
A Shortcut</h2>
To make things a little easier you can create a windows shortcut. This allows you to change the console window size, font and color, too. The following pictures show the particular steps:<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-kH_r1FOl1-Y/UBbthYuGmtI/AAAAAAAAC9M/yM3lUnf3FyE/s1600/01-create-link.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="200" src="http://4.bp.blogspot.com/-kH_r1FOl1-Y/UBbthYuGmtI/AAAAAAAAC9M/yM3lUnf3FyE/s200/01-create-link.png" width="195" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Step 1: Create A Shortcut</td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-9t-OABZ-atA/UBbth7CDWHI/AAAAAAAAC9Q/hqhCkAWe97s/s1600/02-define-target.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="146" src="http://3.bp.blogspot.com/-9t-OABZ-atA/UBbth7CDWHI/AAAAAAAAC9Q/hqhCkAWe97s/s200/02-define-target.png" width="200" /></a></td></tr>
<tr align="center"><td class="tr-caption">Step 2: Select the PHP binary</td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-3vcw4x3uHuA/UBbtiS15vFI/AAAAAAAAC9Y/Lfik8dn7wbA/s1600/03-define-name.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="146" src="http://1.bp.blogspot.com/-3vcw4x3uHuA/UBbtiS15vFI/AAAAAAAAC9Y/Lfik8dn7wbA/s200/03-define-name.png" width="200" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Step 3: Name the shortcut</td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-rfpy62R1Hac/UBbti7v53qI/AAAAAAAAC9g/Zg25u_21Sl0/s1600/04-change-properties.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="200" src="http://2.bp.blogspot.com/-rfpy62R1Hac/UBbti7v53qI/AAAAAAAAC9g/Zg25u_21Sl0/s200/04-change-properties.png" width="140" /></a></td></tr>
<tr align="center"><td class="tr-caption">Step 4: Change command and working directory</td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-QkEKUh3dUVk/UBbtjYhqcuI/AAAAAAAAC9o/Whd5UCnbGCY/s1600/05-change-size.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="200" src="http://4.bp.blogspot.com/-QkEKUh3dUVk/UBbtjYhqcuI/AAAAAAAAC9o/Whd5UCnbGCY/s200/05-change-size.png" width="140" /></a></td></tr>
<tr align="center"><td class="tr-caption">Step 5: Change the window size</td></tr>
</tbody></table>ThWhttp://www.blogger.com/profile/12009821760062911474noreply@blogger.com2tag:blogger.com,1999:blog-3653745335475875863.post-35615892549078661032012-07-14T20:57:00.000+02:002012-07-14T20:57:12.569+02:00Matching Classes In XSLTA while ago (well 2 years) I posted an <a href="http://www.a-basketful-of-papayas.net/2010/04/css-selectors-and-xpath-expressions.html">entry </a>about the differences between CSS selectors and Xpath expressions. An simple "<tt>.classname</tt>" in CSS is a noisy "<tt>contains(concat(' ', normalize-space(@class), ' '), ' first ')"</tt> in Xpath.<br />
<br />
In XSLT this adds a lot of overhead. But if your XSLT processor supports <a href="http://www.exslt.org/">EXSLT</a> you are able to encapsulate it into a function.<br />
<br />
I put this into a file "contains-token.xsl".<br />
<br />
<code><br />
<?xml version="1.0" encoding="UTF-8"?><br />
<xsl:stylesheet<br />
version="1.0"<br />
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"<br />
xmlns:func="http://exslt.org/functions"<br />
extension-element-prefixes="func"<br />
><br />
<func:function name="func:contains-token"><br />
<xsl:param name="haystack"><br />
<xsl:param name="needle"><br />
<xsl:variable name="normalizedHaystack" select="concat(' ', normalize-space($haystack), ' ')"/><br />
<xsl:variable name="normalizedNeedle" select="concat(' ', normalize-space($needle), ' ')"/><br />
<func:result select="$needle != '' and contains($haystack, $needle)"/><br />
</func:function><br />
<br />
</xsl:stylesheet><br />
</code><br />
<br />
You need to declare the func namespace and define it as an extension prefix. The function definition is not unlike an normal template definition. You just have to use the "func:function" and "func:result" elements. Function always need to be inside an namespace. If in doubt, just use the "func" namespace itself.<br />
<br />
In this case I normalize both parameters first and just validate if the haystack contains the needle.<br />
<br />
Now it is possible to import the function and use it.<br />
<br />
<code><br />
...<br />
<xsl:import href="../functions/contains-token.xsl"/><br />
<br />
<xsl:template match="/"><br />
...<br />
<xsl:value-of select=".//div[func:contains-token(@class, 'classname')]"/><br />
...<br />
</xsl:template><br />
...<br />
</code><br />
<br />
It is still longer then the CSS version but it is a lot more readable.<br />
<br />
<br />ThWhttp://www.blogger.com/profile/12009821760062911474noreply@blogger.com0tag:blogger.com,1999:blog-3653745335475875863.post-81704879905450260092012-04-19T09:48:00.000+02:002013-12-12T18:19:54.308+01:00Twin Stem AdaptersI don't like the standard Brompton handlebar. They are to short and to flexible. They are so short that I had to cut of a considerable amount of the right grip to fit the brake lever and the shifter. Not an ideal situation. The S Type is to low for me. One of the users in the German <a href="http://www.bromptonauten.de/forum.html" rel="nofollow">Bromptonauten forum</a> posted an image of his modification. An M Type stem with a stem adapter raising a standard flat handlebar. <br /><br />So I replaced the M Type stem on my Brompton with an H Type. It is 6cm higher, but the hinge is higher, too. I tested three stem adapters.<br /><br /><br /><table class="contentTable" style="width: 100%;"> <tbody><tr class="odd"> <th><br /></th> <th>TranzX double clamp adapter</th> <th>SATORI Aberhallo Adjustable Stem</th> <th>GUB G-80 folding bike stem</th> </tr><tr class="even"> <th style="text-align: left;">Images</th> <td><a href="http://2.bp.blogspot.com/-v3oHZ1jJXQM/T4-_YjoezsI/AAAAAAAAC4A/gGjJh1_tjPs/s1600/IMG_20120419_090340.jpg"><img border="0" height="120" src="http://2.bp.blogspot.com/-v3oHZ1jJXQM/T4-_YjoezsI/AAAAAAAAC4A/gGjJh1_tjPs/s200/IMG_20120419_090340.jpg" width="90" /></a></td> <td><a href="http://1.bp.blogspot.com/-IDWfGYmDehc/T41_Ki0viGI/AAAAAAAAC3c/-ChcKpnc0wU/s1600/IMG_20120417_163107.jpg"><img border="0" height="120" src="http://1.bp.blogspot.com/-IDWfGYmDehc/T41_Ki0viGI/AAAAAAAAC3c/-ChcKpnc0wU/s120/IMG_20120417_163107.jpg" width="90" /></a></td> <td><a href="http://2.bp.blogspot.com/-KTvHDyZ5PkM/T41_f6IjNoI/AAAAAAAAC3o/ISORjGuVwX0/s1600/IMG_20120417_115615.jpg"><img border="0" height="120" src="http://2.bp.blogspot.com/-KTvHDyZ5PkM/T41_f6IjNoI/AAAAAAAAC3o/ISORjGuVwX0/s120/IMG_20120417_115615.jpg" width="90" /></a></td> </tr><tr class="odd"> <th style="text-align: left;">Distance</th> <td>50 mm</td> <td>50 mm</td> <td>55 mm</td> </tr><tr class="even"> <th style="text-align: left;">Weight</th> <td>188g</td> <td>198g</td> <td>141g</td> </tr><tr class="odd"> <th style="text-align: left;">Colors</th> <td>black</td> <td>black</td> <td>red, silver, blue, black, gold</td> </tr><tr class="even"> <th style="text-align: left;">Remove Grip and break lever for assembly</th> <td>No</td> <td>No</td> <td>Yes</td> </tr></tbody></table><br /><br /><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; margin-left: 1em; text-align: right;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-81yDpZng1UE/T4_A6Pkt0QI/AAAAAAAAC4M/q4smwVtEnho/s1600/IMG_20120418_192140.jpg" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" height="200" src="http://3.bp.blogspot.com/-81yDpZng1UE/T4_A6Pkt0QI/AAAAAAAAC4M/q4smwVtEnho/s200/IMG_20120418_192140.jpg" width="150" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">H Type stem, GUB G-80 adapter and flat handle bar</td></tr></tbody></table>The major disadvantage of the TranzX adapter are the clamps above the handle bar. The GUB adapter is similiar but the clamps are at the bottom and smaller. The Aberhallo adapter has a different design - it is the most flexible. TranxZ and Aberhallo use plastic barends, but the GUB has threaded aluminium barends, providing a small storage space. <br /><br />For now I am using the GUB G-80. The 5mm more make a difference (no conflict with the mudguard stay) and I like the look of the bands at the handle bar.<br /><br />The handle bar is 1 meter from the ground and 8 cm lower then the saddle.ThWhttp://www.blogger.com/profile/12009821760062911474noreply@blogger.com1tag:blogger.com,1999:blog-3653745335475875863.post-29273260835606659992012-04-14T16:08:00.000+02:002013-12-12T18:19:54.317+01:00Einladung zum Kölner RadverkehrstreffFast hätte ich es gar nicht mitbekommen, diesmal <a href="http://www.presseportal.de/polizeipresse/pm/12415/2233279/pol-k-120412-3-k-koelner-radverkehrstreff-einladung-fuer-buergerinnen-und-buerger">lädt die Polizei zum Kölner Radverkehrstreff ein</a>. Die Pressemitteilung ist vom Donnerstag den 12.04.2012 - die Veranstaltung wird am Montag, den 16.04.2012 um 18:00 Uhr im Polizeipräsidium Köln (Walter-Pauli-Ring 2-6, Forum 1+2) stattfinden. Großzügige <b>VIER </b>Tage Zeit gibt man also den "<i>interessierten Bürgerinnen und Bürgern</i>" den Termin einzuplanen.<br /><br />Die Stadt hat es bis heute (Samstag, 14.04.2012 16:00 Uhr) nicht geschafft eine entsprechende Meldung <a href="http://www.stadt-koeln.de/4/verkehr/radverkehr/news/">auf ihrer Radverkehrsseite</a> zu veröffentlichen und um Veranstaltungskalender ist sie auch nicht zu finden. Beim letzten Mal wurde die Ankündigung als <a href="http://www.stadt-koeln.de/4/verkehr/radverkehr/news/09428/">Schlussabsatz in einer anderen Meldung</a> untergebracht.<br /><br />Das Thema ist passender weise diesmal "<i>Fahrradkommunikation und Öffentlichkeitsarbeit</i>".<br /><br />ThWhttp://www.blogger.com/profile/12009821760062911474noreply@blogger.com0tag:blogger.com,1999:blog-3653745335475875863.post-32640663355283036272012-03-23T17:02:00.000+01:002013-12-12T18:19:54.325+01:00Brompton ModificationsI don't like the plastic silver look of the Brompton fenders. So I got some carbon fibre effect foil and a hairdryer. The result are some nice black fenders.<br /><br /><div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/-wmIeK9gzG1M/T2wpQhGMabI/AAAAAAAAC0k/aSxQL8QjvpA/s1600/IMG_20120323_083039.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="http://1.bp.blogspot.com/-wmIeK9gzG1M/T2wpQhGMabI/AAAAAAAAC0k/aSxQL8QjvpA/s320/IMG_20120323_083039.jpg" width="240" /></a></div><br />You can see my new <a href="http://tw.myblog.yahoo.com/bikefun-bikefun/article?mid=11023&prev=11464" rel="nofollow">rear suspension</a> on the image, too. Here is a real steel spring inside. So it is nothing for weight weenies. :-)<br /><br /><div class="separator" style="clear: both; text-align: center;"><a href="http://2.bp.blogspot.com/-gGFH_RIDBSU/T2wplBWSkxI/AAAAAAAAC0w/8BiEYj1bvaA/s1600/IMG_20120322_104549.jpg" imageanchor="1" style="margin-bottom: 1em; margin-right: 1em;"><img border="0" height="150" src="http://2.bp.blogspot.com/-gGFH_RIDBSU/T2wplBWSkxI/AAAAAAAAC0w/8BiEYj1bvaA/s200/IMG_20120322_104549.jpg" width="200" /></a> <a href="http://2.bp.blogspot.com/-HOWRaR2H62o/T2wplWG6XWI/AAAAAAAAC0s/lf5nAikstRw/s1600/IMG_20120322_110237.jpg" imageanchor="1" style="margin-bottom: 1em; margin-right: 1em;"><img border="0" height="149" src="http://2.bp.blogspot.com/-HOWRaR2H62o/T2wplWG6XWI/AAAAAAAAC0s/lf5nAikstRw/s149/IMG_20120322_110237.jpg" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/-X2i4V2RTXx0/T2wpPmgP1rI/AAAAAAAAC0U/t0U5ZXMCMgk/s1600/IMG_20120323_083005.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="http://1.bp.blogspot.com/-X2i4V2RTXx0/T2wpPmgP1rI/AAAAAAAAC0U/t0U5ZXMCMgk/s400/IMG_20120323_083005.jpg" width="300" /></a></div><br />ThWhttp://www.blogger.com/profile/12009821760062911474noreply@blogger.com1tag:blogger.com,1999:blog-3653745335475875863.post-31662687816797208342012-03-13T12:14:00.000+01:002013-12-12T18:19:54.335+01:00Brompton Mini O-Bag<br /><div class="separator" style="clear: both; text-align: center;"></div><div class="" style="clear: both; text-align: left;">It is an <a href="http://www.ortlieb.de/_prod.php?lang=en&produkt=ultimate5classic">Ortlieb Ultimate Classic</a> with two differences. First of course the Klickfix mounting adapter is replaced by a Brompton version.</div><div class="" style="clear: both; text-align: left;">The other difference is that it has a handle on to top and no push buttons for map cases. This makes sense because on a Brompton the bag is to low.<br /></div><div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/-k46D2KjRTA0/T18lW9FWXgI/AAAAAAAACyM/NiiVCwsaCH0/s1600/IMG_20120313_113821.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="http://3.bp.blogspot.com/-k46D2KjRTA0/T18lW9FWXgI/AAAAAAAACyM/NiiVCwsaCH0/s400/IMG_20120313_113821.jpg" width="300" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="http://4.bp.blogspot.com/-U3sGdBP64SE/T18lZdbsg3I/AAAAAAAACyc/RXuIoju-IUY/s1600/IMG_20120313_113942.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="http://4.bp.blogspot.com/-U3sGdBP64SE/T18lZdbsg3I/AAAAAAAACyc/RXuIoju-IUY/s400/IMG_20120313_113942.jpg" width="400" /></a></div><div style="clear: both;"><br /></div><div style="clear: both;">Update: Yes, the camera insert fits.</div><div class="separator" style="clear: both; text-align: center;"><a href="http://2.bp.blogspot.com/-TfsmYn3z-O4/T2G7e2sAI4I/AAAAAAAACyk/JtHGcwzq9ig/s1600/IMG_20120315_104451.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="http://2.bp.blogspot.com/-TfsmYn3z-O4/T2G7e2sAI4I/AAAAAAAACyk/JtHGcwzq9ig/s400/IMG_20120315_104451.jpg" width="400" /></a></div><br /><span id="goog_509391239"></span><span id="goog_509391240"></span>ThWhttp://www.blogger.com/profile/12009821760062911474noreply@blogger.com0tag:blogger.com,1999:blog-3653745335475875863.post-86279350834272132722012-02-02T12:25:00.000+01:002013-12-12T18:19:54.354+01:00My New BromptonYeah I got one. It is a modified Brompton from <a href="http://www.junik-hpv.de/">Junik-HPV</a>. It has a Shimano Alfine 11, which makes it really nice to ride.<br /><br /><a href="http://1.bp.blogspot.com/-Ny2xshw_MrU/TyfEXDSyE4I/AAAAAAAACv4/fDBRhrpa2X8/s1600/IMG_20120131_113126.jpg" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="177" src="http://1.bp.blogspot.com/-Ny2xshw_MrU/TyfEXDSyE4I/AAAAAAAACv4/fDBRhrpa2X8/s200/IMG_20120131_113126.jpg" width="200" /></a><br />It is quite different from my Strida. The riding position is not as upright as on the Strida, but still comfortable and I finally have room for my knees. The folding is more complex and the resulting package is smaller but wider. The Strida is easier to transport while folded.<br /><br />The folding of the Brompton is done in five steps plus one optional step for the pedal. The folding of the Strida needs only a single step with four optional steps for the both handle bars and pedals.<br /><br />Brompton folding steps:<br /><ol><li> Move the right pedal in position</li><li>Fold the rear </li><li>Unscrew and fold the front</li><li>Push the saddle into the frame</li><li>Unscrew and fold the stem</li><li>Optional: fold the left pedal</li></ol>Strida folding steps:<br /><ol><li>Fold the bottom tube up and the wheels against each other</li><li> Optional: Left handle bar</li><li>Optional: Right handle bar</li><li>Optional: Left pedal</li><li>Optional: Right pedal</li></ol>The luggage options on a Brompton are a lot better then on the Strida. The front block is extremly useful and allows to transport a lot of stuff. It is connected to the frame, not the stem, so it does not affect the steering. You can add something comparable to the Strida using a Klick Fix Caddy. But on the Strida this makes the steering more difficult depending on the load.<br /><br />Unlike the Strida the Brompton has a dynamo hub. So now I have light without thinking about batteries. I am thinking about an adapter so I can use it to charge devices during tours.<br /><br /><br /><br /><br /><br />ThWhttp://www.blogger.com/profile/12009821760062911474noreply@blogger.com0tag:blogger.com,1999:blog-3653745335475875863.post-20476545151789135542012-01-16T12:15:00.000+01:002013-12-12T18:19:54.363+01:00The Rhine, Yesterday<div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-P127oV0459o/TxQFzyCrhdI/AAAAAAAACvc/TqKWkJYf5e0/s1600/IMG_20120115_153646.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="300" src="http://1.bp.blogspot.com/-P127oV0459o/TxQFzyCrhdI/AAAAAAAACvc/TqKWkJYf5e0/s400/IMG_20120115_153646.jpg" width="400" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">The Rhine - the other bank is hidden by the fog</td></tr></tbody></table>ThWhttp://www.blogger.com/profile/12009821760062911474noreply@blogger.com0tag:blogger.com,1999:blog-3653745335475875863.post-43953675661904632402011-12-28T11:30:00.000+01:002013-12-12T18:19:54.372+01:00Winterdienst<p>Beim letzten Radverkehrstreff hier in Köln wurde ganz stolz das neue Winterdienstkonzept erwähnt. Demnach bekommt das Räumen der Radwege eine hohe Priorität. Die Sache hat nur einen Haken. Die <a href="http://www.stadt-koeln.de/4/verkehr/radverkehr/news/09783/">Webseite der Stadt</a> weist darauf hin:</p> <blockquote><p>Straßenreinigungssatzung</p> <p>Dort, wo der Gehweg von der Grundstückseigentümerin oder dem Grundstückseigentümer geräumt werden muss, gilt dies in folgenden Fällen auch für den Radweg:</p> <ul> <li>Er muss auf der gleichen Ebene wie der Gehweg liegen.</li> <li>Er darf nur durch Markierung oder Materialwechsel kenntlich gemacht sein.</li></ul> </blockquote> <p>Wollte man die Hochbordadwege nutzen muss man also darauf hoffen das der Anlieger schon vor einem aufgestanden ist, und sein Stückchen Weg geräumt hat. Nun haben aber die meisten Straßen nicht einen Anlieger, sondern viele. Wenn auch das erste sichtbare Stückchen geräumt ist, kann dies für das nächste Haus schon wieder ganz anders sein.</p> <p>Durch diese Regelung kann man sich auf eine Räumung der Radwege nicht verlassen. Dies macht sie meiner Meinung nach <b>unbenutzbar</b>.</p> <p>Es ist ein weiteres Argument gegen Hochboardradwege. Fahrbahnen werden durch den Winterdienst geräumt. Ein Rad- oder Schutzstreifen würde somit mit geräumt werden. Vor allem würde jedoch die Fahrbahn zumindest bis zur nächsten Kreuzung durchgängig geräumt sein und nicht plötzlich mitten drin mal nicht.</p>ThWhttp://www.blogger.com/profile/12009821760062911474noreply@blogger.com0tag:blogger.com,1999:blog-3653745335475875863.post-52025379009709490712011-10-03T13:54:00.000+02:002013-12-12T18:19:54.381+01:00You Will Look Sweet Upon The Seat<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://racheldsc.wordpress.com/2011/01/05/my-freelance-illustration-year-in-review-acofi/" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://3.bp.blogspot.com/-TeUA9L72zMI/TomhQbM8rkI/AAAAAAAACtQ/6U0Q6MiBbxg/s1600/bicycle-london-transport-fashion-rachel-de-ste-croix-precious-little.jpg" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">created by <a href="http://twitter.com/precious_little"><span class="fn">Rachel de Ste. Croix</span></a></td></tr></tbody></table>Found the two Stridas?ThWhttp://www.blogger.com/profile/12009821760062911474noreply@blogger.com0