<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>JNotes &#124; Thoughts and Notes of a Web Developer from Hamburg, Germany</title>
	<atom:link href="http://jnotes.jonasfischer.net/feed/" rel="self" type="application/rss+xml" />
	<link>http://jnotes.jonasfischer.net</link>
	<description>The Tech Blog of Jonas Fischer, a Web Developer from Hamburg, Germany. Main topics: PHP, Databases, Symfony, Zend Framework</description>
	<lastBuildDate>Sat, 11 Jun 2011 06:48:13 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>jQuery Mobile ajax loading spinner animation</title>
		<link>http://jnotes.jonasfischer.net/2011/06/jquery-mobile-ajax-loading-spinner-animation/</link>
		<comments>http://jnotes.jonasfischer.net/2011/06/jquery-mobile-ajax-loading-spinner-animation/#comments</comments>
		<pubDate>Sat, 11 Jun 2011 06:46:52 +0000</pubDate>
		<dc:creator>Jonas Fischer</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[CSS3]]></category>
		<category><![CDATA[jQuery Mobile]]></category>

		<guid isPermaLink="false">http://jnotes.jonasfischer.net/?p=347</guid>
		<description><![CDATA[Some people seem to be a little bit unhappy with the way the default jQuery Mobile loading ajax spinner rotates. Luckily, this can be changed easily by contorlling the css animation settings. Here are three simple variations.
Works only in Safari and Chrome:




      variation 1 (jQuery Mobile standard):



    [...]]]></description>
			<content:encoded><![CDATA[<p>Some people seem to be a little bit unhappy with the way the default jQuery Mobile loading ajax spinner rotates. Luckily, this can be changed easily by contorlling the css animation settings. Here are three simple variations.</p>
<h3>Works only in Safari and Chrome:</h3>
<p></p>
<link rel="stylesheet" href="/wp-content/uploads/demos/jqm-spin/spin.css" />
<div class="jqm-spin-demo">
<div>
      variation 1 (jQuery Mobile standard):</p>
<div class="ui-icon ui-icon-loading spin var1"></div>
</p></div>
<div>
      variation 2 (ease-in-out spun over multiple rotations):</p>
<div class="ui-icon ui-icon-loading spin var2"></div>
</p></div>
<div>
      variation 3 (linear):</p>
<div class="ui-icon ui-icon-loading spin var3"></div>
</p></div>
</p></div>
<h3>CSS for variations 2 and 3:</h3>
<pre class="brush: css">
.jqm-spin-demo .spin.var2 {
  -webkit-animation-name: spin_var2;
	-webkit-animation-duration: 12s;
}

@-webkit-keyframes spin_var2 {
	from {-webkit-transform: rotate(0deg);}
  	to {-webkit-transform: rotate(3600deg);}
}

.jqm-spin-demo .spin.var3 {
  -webkit-animation-timing-function: linear;
}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://jnotes.jonasfischer.net/2011/06/jquery-mobile-ajax-loading-spinner-animation/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Phonegap 0.9.5 orientationchange javascript event</title>
		<link>http://jnotes.jonasfischer.net/2011/06/phonegap-0-9-5-orientationchange-javascript-event/</link>
		<comments>http://jnotes.jonasfischer.net/2011/06/phonegap-0-9-5-orientationchange-javascript-event/#comments</comments>
		<pubDate>Wed, 01 Jun 2011 11:25:39 +0000</pubDate>
		<dc:creator>Jonas Fischer</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[jQuery]]></category>
		<category><![CDATA[jQuery Mobile]]></category>
		<category><![CDATA[Phonegap]]></category>

		<guid isPermaLink="false">http://jnotes.jonasfischer.net/?p=342</guid>
		<description><![CDATA[Since Phonegap version 0.9.5 the orientationchange event is triggered on the document object instead of the window object. This might be a problem as most libraries (amongst jQueryMobile) expect the event to occur on the window object.
However, there is an easy solution (this one requires jQuery but can be easily adapted):

$(document).bind(&#34;orientationchange&#34;, function(event){
  $(window).trigger(event);
});

]]></description>
			<content:encoded><![CDATA[<p>Since Phonegap version 0.9.5 the orientationchange event is triggered on the document object instead of the window object. This might be a problem as most libraries (amongst jQueryMobile) expect the event to occur on the window object.</p>
<p>However, there is an easy solution (this one requires jQuery but can be easily adapted):</p>
<pre class="brush: javascript">
$(document).bind(&quot;orientationchange&quot;, function(event){
  $(window).trigger(event);
});
</pre>
]]></content:encoded>
			<wfw:commentRss>http://jnotes.jonasfischer.net/2011/06/phonegap-0-9-5-orientationchange-javascript-event/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>jfCachePlugin: two Filters that might help to make your pages cacheable (client-side)</title>
		<link>http://jnotes.jonasfischer.net/2011/03/jfcacheplugin-two-filters-that-might-help-to-make-your-pages-cacheable-client-side/</link>
		<comments>http://jnotes.jonasfischer.net/2011/03/jfcacheplugin-two-filters-that-might-help-to-make-your-pages-cacheable-client-side/#comments</comments>
		<pubDate>Sun, 06 Mar 2011 16:45:06 +0000</pubDate>
		<dc:creator>Jonas Fischer</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Cache]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[Symfony]]></category>
		<category><![CDATA[Symfony Plugin]]></category>

		<guid isPermaLink="false">http://jnotes.jonasfischer.net/?p=330</guid>
		<description><![CDATA[Today I packaged two filters I am using to manage client side caching:

jfCacheHeadersFilter: sets the HTTP headers required to server pages from client side (or proxy) cache
jfCacheInvalidateStaticSourceUrlsFilter: append a cache key to the URLs of referenced Javascript and CSS files so that you can configure a very long cache expire time for those files

Installation Instructions
Check [...]]]></description>
			<content:encoded><![CDATA[<p>Today I packaged two filters I am using to manage client side caching:</p>
<ul>
<li>jfCacheHeadersFilter: sets the HTTP headers required to server pages from client side (or proxy) cache</li>
<li>jfCacheInvalidateStaticSourceUrlsFilter: append a cache key to the URLs of referenced Javascript and CSS files so that you can configure a very long cache expire time for those files</li>
</ul>
<h2><span id="more-330"></span>Installation Instructions</h2>
<h3>Check out the source code</h3>
<pre class="brush: sh">

$ svn co http://jfcacheplugin.googlecode.com/svn/branches/1.4.0 plugins/jfCachePlugin
</pre>
<h3>Publish assets</h3>
<pre class="brush: sh">

$ ./symfony plugin:publish-assets
</pre>
<h3>Enable filters in your filters.yml</h3>
<h4>jfCacheHeadersFilter</h4>
<p>This filter automatically sets the correct cache headers so that web browsers and proxy servers can cache your pages.<br />
The corresponding cache headers will only be set if<br />
a) you call jfCacheHeadersFilter::enable() or<br />
b) you have enabled the symfony file cache with the &#8220;with_layout&#8221; option for the pages you want to cache (see <a href="http://www.symfony-project.org/gentle-introduction/1_4/en/12-Caching" target="_blank">http://www.symfony-project.org/gentle-introduction/1_4/en/12-Caching</a>)</p>
<p>In either case you can avoid the cache headers if you call jfCacheHeadersFilter::disable()</p>
<p>Put these settings in your filters.yml file:</p>
<pre class="brush: php">
jfCacheHeaders:
  class: jfCacheHeadersFilter
  param:
    cacheableEnvironments: [prod]
    cacheableHttpStatusCodes: [200]
    expireTime: 300
</pre>
<h4>jfCacheHeadersFilter</h4>
<p>This filter automatically appends a cache=TIMESTAMP_OF_LAST_CLEAR_CACHE parameter to referenced .css and .js files.<br />
Therefore you can cache your css/js files for a very long time without worrying that your users might use old versions of your files.<br />
Clearing the symfony cache will automatically &#8220;clear&#8221; all js/css files cached in Browsers or Proxys.</p>
<p>Put these settings in your filters.yml file:</p>
<pre class="brush: php">
jfCacheInvalidateStaticSourceUrls:
  class: jfCacheInvalidateStaticSourceUrlsFilter
  param:
    cacheKeyName: cache #name of the url parameter the gets appended to each css/js URL
</pre>
<h4>optional: modify vhost/.htaccess settings to server js/css files from cache</h4>
<p>Here is a sample vhost/.htaccess config to make sure that js and css files get cached properly (for 30 days in this example):</p>
<pre class="brush: php">&lt;ifmodule mod_expires.c&gt;
  ExpiresActive On
  ExpiresByType application/x-javascript A2592000
  ExpiresByType application/javascript A2592000
  ExpiresByType text/javascript A2592000
  ExpiresByType text/css A2592000
&lt;/ifmodule&gt;

&lt;ifmodule mod_headers.c&gt;
  &lt;filesmatch &quot;\.(css|js)$&quot;&gt;
    Header set Cache-Control &quot;max-age=2592000, public&quot;
  &lt;/filesmatch&gt;
&lt;/ifmodule&gt;
</pre>
]]></content:encoded>
			<wfw:commentRss>http://jnotes.jonasfischer.net/2011/03/jfcacheplugin-two-filters-that-might-help-to-make-your-pages-cacheable-client-side/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>jfPortableDevicePlugin: Symfony to go</title>
		<link>http://jnotes.jonasfischer.net/2011/02/jfportabledeviceplugin-symfony-to-go/</link>
		<comments>http://jnotes.jonasfischer.net/2011/02/jfportabledeviceplugin-symfony-to-go/#comments</comments>
		<pubDate>Sun, 27 Feb 2011 14:57:19 +0000</pubDate>
		<dc:creator>Jonas Fischer</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[jQuery Mobile]]></category>
		<category><![CDATA[mobile]]></category>
		<category><![CDATA[Symfony]]></category>

		<guid isPermaLink="false">http://jnotes.jonasfischer.net/?p=317</guid>
		<description><![CDATA[I wrote a symfony plugin that allows you to easily optimize your existing symfony application for mobile devices using jQuery Mobile.
And if you want to distribute your application in Android Market or Apple App Store it is possible using e.g. Phonegap.
If you wish to see the plugin in action you can visit the mobile version [...]]]></description>
			<content:encoded><![CDATA[<p>I wrote a symfony plugin that allows you to easily optimize your existing symfony application for mobile devices using <a href="http://jquerymobile.com" target="_blank">jQuery Mobile</a>.<br />
And if you want to distribute your application in Android Market or Apple App Store it is possible using e.g. <a href="http://www.phonegap.com/" target="_blank">Phonegap</a>.</p>
<p>If you wish to see the plugin in action you can visit the mobile version of my web page <a href="http://www.jonasfischer.net/m" target="_blank">http://www.jonasfischer.net/m.</a></p>
<p>If you are visiting my website using one of the supported operating systems (e.g. iOs or Android) then you will be prompted if you wish to view the mobile-enhanced version or the regular version: <a href="http://www.jonasfischer.net/" target="_blank">http://www.jonasfischer.net/</a>.</p>
<p><span id="more-317"></span></p>
<h2>Features</h2>
<ul>
<li>jQuery Mobile integration</li>
<li>jfPortableDevicePlugin is bundled with a standard template and theme to get your app up and running within minutes</li>
<li>automatically &#8220;hijacks&#8221; links and appends some parameters to the URLs so that you can apply special logic in your symfony actions/templates if needed</li>
<li>flexible and extendable using symfony standards: The templates are organized in a very fine-grained manner so that you can override the parts that don&#8217;t fit your needs.</li>
<li>Comes with a phonegap build-mode option to prepare your app for deployment using phonegap.</li>
</ul>
<h2>Installation instructions</h2>
<h3>Checkout the Source Code:</h3>
<pre class="brush: sh">
$ svn co http://jfportabledevice.googlecode.com/svn/trunk plugins/jfPortableDevicePlugin
</pre>
<p>Be aware that you are checking out the trunk. As this plugin is still under heavy development (in fact, the jQuery Mobile library itself is still in alpha state) there is no stable tag/branch, yet.</p>
<h3>Enable plugin jfPortableDevicePlugin in your ProjectConfiguration.php</h3>
<p>As Victor points out in the comments, if you have not enabled all plugins by default, you need to enabled jfPortableDevicePlugin manually:</p>
<pre class="brush: php">
$this-&gt;enablePlugins(’jfPortableDevicePlugin’);
</pre>
<h3>Enable module jfPortableDevice in your settings.yml</h3>
<pre class="brush: php">
enabled_modules: [..., jfPortableDevice]
</pre>
<h3>Publish plugin assets</h3>
<pre class="brush: sh">
$ php symfony plugin:publish-assets
</pre>
<h3>Enabled filters in your filters.yml</h3>
<h4>required: jfPortableDeviceFilter</h4>
<p>This filter automatically sets the correct layout for portable device requests.</p>
<pre class="brush: php">
jfPortableDevice:
class:  jfPortableDeviceFilter
param:
layout: jfPortableDeviceLayout
</pre>
<p>Additionally, you need to ensure that the specified layout file exists in your app&#8217;s layout folder.<br />
To get up and running you can use the bundled jfPortableDeviceLayout.php file. Simply symlink or copy it into your app&#8217;s layout folder:</p>
<pre class="brush: sh">
$ ln -s ../../../plugins/jfPortableDevicePlugin/templates/jfPortableDeviceLayout.php apps/frontend/templates/jfPortableDeviceLayout.php
</pre>
<p>or</p>
<pre class="brush: sh">
$ cp plugins/jfPortableDevicePlugin/templates/jfPortableDeviceLayout.php apps/frontend/templates/
</pre>
<h4>optional: redirectByUserAgentPortableDevice</h4>
<p>This filter redirects to the portable device controller if a request was made from one of the specified user agents</p>
<pre class="brush: php">
redirectByUserAgentPortableDevice:
  class:  redirectByUserAgentPortableDeviceFilter
  param:
    redirectUrl: /m/#%s?jfPD_device=portable
    noHeaderRedirect: true
    javascriptConfirm: true
    stopParameters:
      - noMobileRedirect
    stopModules:
      - jfPortableDevice
    userAgents:
      - iphone #really bad example string because it might easily be miss-matched. Choose whichever regular expressions fit your needs
      - ipod #really bad example string because it might easily be miss-matched. Choose whichever regular expressions fit your needs
      - ipad #really bad example string because it might easily be miss-matched. Choose whichever regular expressions fit your needs
      - android #really bad example string because it might easily be miss-matched. Choose whichever regular expressions fit your needs
</pre>
<h3>Clear the symfony cache</h3>
<pre class="brush: sh">
$ php symfony cc
</pre>
]]></content:encoded>
			<wfw:commentRss>http://jnotes.jonasfischer.net/2011/02/jfportabledeviceplugin-symfony-to-go/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Allow empty selection in sfWidgetFormDoctrineChoice / Unline doctrine relations</title>
		<link>http://jnotes.jonasfischer.net/2010/10/allow-empty-selection-in-sfwidgetformdoctrinechoice-unline-doctrine-relations/</link>
		<comments>http://jnotes.jonasfischer.net/2010/10/allow-empty-selection-in-sfwidgetformdoctrinechoice-unline-doctrine-relations/#comments</comments>
		<pubDate>Tue, 12 Oct 2010 07:37:20 +0000</pubDate>
		<dc:creator>Jonas Fischer</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Doctrine]]></category>
		<category><![CDATA[Symfony]]></category>

		<guid isPermaLink="false">http://jnotes.jonasfischer.net/?p=312</guid>
		<description><![CDATA[The standard sfWidgetFormDoctrineChoice &#8220;add_empty&#8221; option is only useful if the relation has not yet been set. However, once a relation is saved, selecting the empty value again will result in an validation error.
Here is a simple way to allow users to unlink relations by selecting the empty value of a sfWidgetFormDoctrineChoice select list. Simply add [...]]]></description>
			<content:encoded><![CDATA[<p>The standard sfWidgetFormDoctrineChoice &#8220;add_empty&#8221; option is only useful if the relation has not yet been set. However, once a relation is saved, selecting the empty value again will result in an validation error.</p>
<p>Here is a simple way to allow users to unlink relations by selecting the empty value of a sfWidgetFormDoctrineChoice select list. Simply add these lines to your form&#8217;s configure()-method:</p>
<pre class="brush: php">
$this-&gt;widgetSchema[&#039;rdr_images_list&#039;]-&gt;setOption(&#039;add_empty&#039;, true);
$valuesRaw = sfContext::getInstance()-&gt;getRequest()-&gt;getParameter($this-&gt;getName());
if (1 === count($valuesRaw[&#039;images_list&#039;]) &amp;amp;&amp;amp; &#039;&#039; === $valuesRaw[&#039;images_list&#039;][0]) {
  $this-&gt;validatorSchema[&#039;images_list&#039;] = new sfValidatorPass();
}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://jnotes.jonasfischer.net/2010/10/allow-empty-selection-in-sfwidgetformdoctrinechoice-unline-doctrine-relations/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>New Symfony Helper for jQuery&#8217;s excellent form validation plugin</title>
		<link>http://jnotes.jonasfischer.net/2010/08/new-symfony-helper-for-jquerys-excellent-form-validation-plugin/</link>
		<comments>http://jnotes.jonasfischer.net/2010/08/new-symfony-helper-for-jquerys-excellent-form-validation-plugin/#comments</comments>
		<pubDate>Sat, 21 Aug 2010 17:06:48 +0000</pubDate>
		<dc:creator>Jonas Fischer</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://jnotes.jonasfischer.net/?p=302</guid>
		<description><![CDATA[I updated my old Symfony helper (see http://jnotes.jonasfischer.net/2010/03/smyfony-helper-for-jquerys-excellen-form-validation-plugin/) for the jQuery plugin &#8220;jquery.validate&#8221; (see http://docs.jquery.com/Plugins/Validation). It now supports embedded forms and is much simpler to use:
Usage
Simply insert the following code directly after the opening &#60;form&#62;-Tag:

&#60; ?php $formId = &#039;myFormId&#039; ?&#62;
&#60;form id=&#34;&#60;?php echo $formId ?&#62;&#34;&#62;
&#60; ?php
use_helper(&#039;jQueryValidator&#039;);
echo jquery_validate_form($form, $formId);
?&#62;

This will add some css classes to the form [...]]]></description>
			<content:encoded><![CDATA[<p>I updated my old Symfony helper (see http://jnotes.jonasfischer.net/2010/03/smyfony-helper-for-jquerys-excellen-form-validation-plugin/) for the jQuery plugin &#8220;jquery.validate&#8221; (see http://docs.jquery.com/Plugins/Validation). It now supports embedded forms and is much simpler to use:</p>
<h3>Usage</h3>
<p>Simply insert the following code directly after the opening &lt;form&gt;-Tag:</p>
<pre class="brush: php">
&lt; ?php $formId = &#039;myFormId&#039; ?&gt;
&lt;form id=&quot;&lt;?php echo $formId ?&gt;&quot;&gt;
&lt; ?php
use_helper(&#039;jQueryValidator&#039;);
echo jquery_validate_form($form, $formId);
?&gt;
</pre>
<p>This will add some css classes to the form widgets based on the validators configured in your form.</p>
<p>Then you can add more complex validation afterwards:</p>
<pre class="brush: javascript">
&lt;script&gt;
//Certificate name field is only required if a certificate is requested
$(&#039;#rdr_donation_cert_name&#039;).rules(&#039;add&#039;, {
  required: &#039;#rdr_donation_certificate_1:checked&#039;
});
&lt;/script&gt;
</pre>
<p>In some cases you might want to access the jQuery-validate object itself:</p>
<pre class="brush: php">
&lt;script&gt;
 if (typeof &lt; ?php echo $form-&gt;getName() ?&gt;Validator !== &#039;undefined&#039;) {
  var donationCertificateValidator = &lt; ?php echo $form-&gt;getName() ?&gt;Validator; //jQuery.validate validator (instantiated in jquery_validate_form() helper)
  donationCertificateValidator.element($(&#039;#rdr_donation_cert_name&#039;));
}
&lt;/script&gt;
</pre>
<p><span id="more-302"></span></p>
<h3>Source Code</h3>
<p>jQueryValidatorHelper.php:</p>
<pre class="brush: php">
&lt; ?php
/**
 * Enable the jQuery.validate plugin validation for the given {@param $form}
 *
 * @param sfForm $form [the sfForm object]
 * @param String $formId [the DOM Id of the rendered form]
 * @param String  $jQueryValidatePlugin [the script name of the jquery.validate plugin]
 */
function jquery_validate_form(sfForm $form, $formId, $jQueryValidatePlugin = &#039;jquery/plugins/validate/jquery.validate.js&#039;) {
  sfContext::getInstance()-&gt;getConfiguration()-&gt;loadHelpers(array(&#039;Asset&#039;, &#039;JavascriptBase&#039;));

  if ($jQueryValidatePlugin) {
		use_javascript($jQueryValidatePlugin);
  }

  _jquery_validate_form_extend_form_fields($form-&gt;getValidatorSchema()-&gt;getFields(), $form-&gt;getWidgetSchema()-&gt;getFields());
  return javascript_tag(&quot;var {$form-&gt;getName()}Validator = jQuery(&#039;#$formId&#039;).validate();&quot;);
}

/**
 * Internal helper-helper that adds css and title meta information to the form fields.
 * This information is used by jQuery.validate to build up the validation rules and error messages
 *
 * @param array $validators
 * @param array $widgets
 */
function _jquery_validate_form_extend_form_fields(array $validators, array $widgets)
{
  foreach ($validators as $fieldName =&gt; $validator) {
  	$widget = $widgets[$fieldName];
  	if ($validator instanceof sfValidatorSchema || $validator instanceof sfWidgetFormSchemaDecorator) {
  		_jquery_validate_form_extend_form_fields($validator-&gt;getFields(), $widget-&gt;getFields());
  	}
  	else {
  		$options = $validator-&gt;getOptions();
  		$messages = $validator-&gt;getMessages();
  		$class = $widget-&gt;getAttribute(&#039;class&#039;);
  		$title = $widget-&gt;getAttribute(&#039;title&#039;);
  		if ($options[&#039;required&#039;]) {
  			$class .= &#039; required&#039;;
  			$title .= $messages[&#039;required&#039;];
  		}

  		switch (get_class($validator)) {
  			case &#039;sfValidatorEmail&#039;:
  			  $class .= &#039; email&#039;;
  			  break;
  		}

  		$widget-&gt;setAttribute(&#039;class&#039;, $class);
  		$widget-&gt;setAttribute(&#039;title&#039;, $title);

  	}
  }

}
</pre>
</form>
]]></content:encoded>
			<wfw:commentRss>http://jnotes.jonasfischer.net/2010/08/new-symfony-helper-for-jquerys-excellent-form-validation-plugin/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Symfony Helper for jQuery&#8217;s excellent form validation plugin</title>
		<link>http://jnotes.jonasfischer.net/2010/03/smyfony-helper-for-jquerys-excellen-form-validation-plugin/</link>
		<comments>http://jnotes.jonasfischer.net/2010/03/smyfony-helper-for-jquerys-excellen-form-validation-plugin/#comments</comments>
		<pubDate>Sat, 13 Mar 2010 07:31:50 +0000</pubDate>
		<dc:creator>Jonas Fischer</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://jnotes.jonasfischer.net/?p=284</guid>
		<description><![CDATA[The jQuery plugin &#8220;jquery.validate&#8221; provides an easy way to implement javascript form validation with nice error messages.

Documentation and Download: http://bassistance.de/jquery-plugins/jquery-plugin-validation/
Demo: http://jquery.bassistance.de/validate/demo/

I wrote this little helper method to extract the validation rules from forms defined with the symfon form framework and to automatically create the jquery.validate code to validate those forms on the client side. Of [...]]]></description>
			<content:encoded><![CDATA[<p>The jQuery plugin &#8220;jquery.validate&#8221; provides an easy way to implement javascript form validation with nice error messages.</p>
<ul>
<li>Documentation and Download: <a href="http://bassistance.de/jquery-plugins/jquery-plugin-validation/" target="_blank">http://bassistance.de/jquery-plugins/jquery-plugin-validation/</a></li>
<li>Demo: <a href="http://jquery.bassistance.de/validate/demo/">http://jquery.bassistance.de/validate/demo/</a></li>
</ul>
<p>I wrote this little helper method to extract the validation rules from forms defined with the symfon form framework and to automatically create the jquery.validate code to validate those forms on the client side. Of course, server side validation will still work, too.</p>
<p><span id="more-284"></span></p>
<p>In order to use the helper, you first need to include jQuery and the jQuery.validate plugin. Then you should download</p>
<p><a href="http://jnotes.jonasfischer.net/wp-content/uploads/2010/03/jQueryValidatorHelper.php.txt">jQueryValidatorHelper</a> (and remove the .txt suffix from its filename).</p>
<h3>Usage examples</h3>
<p>1. completely automatically:</p>
<pre class="brush: php">&lt; ?php
use_helper(&#039;jQueryValidator&#039;);
echo jquery_validate_form($form, array(&#039;id&#039; =&gt; &#039;signupForm&#039;));
?&gt;
&lt;form id=&quot;signupForm&quot; method=&quot;post&quot;&gt;
</pre>
<p>1. specify additional fields or rules for validation:</p>
<pre class="brush: php">
use_helper(&#039;jQueryValidator&#039;);
$attributes = array(&#039;id&#039; =&gt; &#039;profileEditForm&#039;);
echo jquery_validate_form($form, $attributes, array(&#039;aupair[baseData][first_name]&#039; =&gt; array(&#039;options&#039; =&gt; array(&#039;required&#039; =&gt; true), &#039;messages&#039; =&gt; array(&#039;required&#039; =&gt; &#039;Required&#039;))));
?&gt;
&lt; ?php echo form_tag_for($form, &#039;@aupairs&#039;, $attributes); ?&gt;
</pre>
<p>The third parameter of jquery_validate_form gives you the possibility to add more fields to validation or to override the automatically created validation rules with the ones fitting your needs better.</p>
<h3>Limitations:</h3>
<ul>
<li>No support for embedded forms (forms in forms won&#8217;t get validated)</li>
<li>Only required, min length, max length and email validation is supported by now. However, you can add other jquery.validate validation options easily if you need them.</li>
</ul>
</form>
]]></content:encoded>
			<wfw:commentRss>http://jnotes.jonasfischer.net/2010/03/smyfony-helper-for-jquerys-excellen-form-validation-plugin/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Symfony Filter: redirect depending on requesting user agent (e.g. detect mobile devices)</title>
		<link>http://jnotes.jonasfischer.net/2009/11/symfony-filter-redirect-useragent/</link>
		<comments>http://jnotes.jonasfischer.net/2009/11/symfony-filter-redirect-useragent/#comments</comments>
		<pubDate>Thu, 26 Nov 2009 15:18:48 +0000</pubDate>
		<dc:creator>Jonas Fischer</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[mobile]]></category>
		<category><![CDATA[Symfony]]></category>

		<guid isPermaLink="false">http://jnotes.jonasfischer.net/?p=261</guid>
		<description><![CDATA[I wrote two filters that allow you to configure redirect rules for requests sent from special user agents e.g. iphone, android &#38; co.
If the requesting user agent matches the redirect rules then the redirect is issued using HTTP Headers. As a fallback for cached pages, the redirect is issued again using javascript (the filter inserts [...]]]></description>
			<content:encoded><![CDATA[<p>I wrote two filters that allow you to configure redirect rules for requests sent from special user agents e.g. iphone, android &amp; co.</p>
<p>If the requesting user agent matches the redirect rules then the redirect is issued using HTTP Headers. As a fallback for cached pages, the redirect is issued again using javascript (the filter inserts some lines of javascript code in the <head>-section of your document).</p>
<p>The first one, redirectUserAgentFilter, is a generic approach to redirects depending on the user agent string. You can activate it in your filters.yml using these lines:</p>
<pre class="brush: php">
redirectUserAgent:
  class:  redirectUserAgentFilter
  enabled: on
  param:
    redirectUrl: http://mobile.yourdomain.com
    userAgents:
      - android
      - iphone
      - ipod
</pre>
<p>In this case, the filter would redirect all requests coming from user agents containing  &#8220;android&#8221;, &#8220;iphone&#8221; oder &#8220;ipod&#8221; in their names to http://mobile.yourdomain.com.<br />
<span id="more-261"></span><br />
redirectUserAgentFilter.class.php:</p>
<pre class="brush: php">
/**
 * Redirect request if it was issued by one of the configured user agents
 * params:
 *  - array userAgents
 *  - string redirectUrl (may contain a %s sprintf placeholder)
 *
 * Example filters.yml integration:
 *
 * &lt;pre&gt;
redirectUserAgent:
  class:  redirectUserAgentFilter
  enabled: on
  param:
    redirectUrl: http://mobile.yourdomain.com
    userAgents:
      - android
      - iphone
      - ipod
&lt;/pre&gt;
 *
 */
class redirectUserAgentFilter extends sfFilter
{
  public function execute($filterChain)
  {

  	// Execute this filter only once
    if ($this-&gt;isFirstCall())
    {
	    $context  = $this-&gt;getContext();
	    $request  = $context-&gt;getRequest();

	    foreach ($this-&gt;getStopParameters() as $stopParameter) {
	    	if ($request-&gt;hasParameter($stopParameter)) {
	    		$filterChain-&gt;execute();
	    		$content = $this-&gt;context-&gt;getResponse()-&gt;getContent();
	    		$content = self::addStopParameterToLinks($content, $stopParameter);
	    		$this-&gt;context-&gt;getResponse()-&gt;setContent($content);
	    		return;
	    	}
	    }

	    $redirectUrl = $this-&gt;getRedirectUrl();
	    $userAgent = strtolower($request-&gt;getHttpHeader(&#039;User-Agent&#039;));
	    $userAgents = $this-&gt;getUserAgents();
	    foreach ($userAgents as $checkAgent) {
	    	if (false !== strpos($userAgent, $checkAgent)) {
		    	if (sfConfig::get(&#039;sf_logging_enabled&#039;))
					{
					  $context-&gt;getLogger()-&gt;info(&quot;Mobile user agent &#039;$checkAgent&#039; detected. Redirecting to &#039;$redirectUrl&#039;&quot;);
					}
	    		$context-&gt;getController()-&gt;redirect($redirectUrl, 0, 302);
	    		throw new sfStopException();
	    	}
	    }
    }

    $filterChain-&gt;execute();

    $content = $this-&gt;context-&gt;getResponse()-&gt;getContent();
    $content = $this-&gt;addJavascriptRedirect($content, $userAgents, $redirectUrl);
    $this-&gt;context-&gt;getResponse()-&gt;setContent($content);

  }

  protected function addJavascriptRedirect($content, $userAgents, $redirectUrl)
  {
  	$script = &#039;
  	&lt;script type=&quot;text/javascript&quot;&gt;
  		var userAgents = &#039;.json_encode($userAgents).&#039;;
  		var userAgent = navigator.userAgent
  		if (userAgent) {
				for(var i=0; i&lt;useragents .length; i++) {
					var regex = new RegExp(userAgents[i], &quot;i&quot;);
					if (regex.exec(userAgent)) {
						document.location.href=decodeURIComponent(&quot;&#039;.urlencode($redirectUrl).&#039;&quot;);
					}
				}
  		}
  	&lt;/script&gt;&#039;;

  	return str_replace(&#039;&lt;head&gt;&#039;, &#039;&lt;/head&gt;&lt;head&gt;&#039;.$script, $content);
  }

  /**
   * We want to preserve the stopParemetrs so we have to append it to all links
   *
   * @param unknown_type $stopParameter
   * @return unknown_type
   */
  public static function addStopParameterToLinks($content, $stopParameter)
  {
    //search and replace URLs
    $pattern = &#039;/(&lt;a .+href|action)=&quot;([^&quot;]+)&quot;/i&#039;;
    $replacement = &#039;$1=&quot;$2?&#039;.$stopParameter.&#039;=1&quot;&#039;;
    $content = preg_replace($pattern, $replacement, $content);

    //search and replace urls that already had an querystring parameter and thus now have two questionmarks in url
    $pattern = &#039;/(&lt;a.+href|action)=&quot;([^?]+)\?(.*)\?&#039;.$stopParameter.&#039;=1&quot;/i&#039;;
    $replacement = &#039;$1=&quot;$2?$3&amp;&#039;.$stopParameter.&#039;=1&quot;&#039;;
    $content = preg_replace($pattern, $replacement, $content);
    return $content;
  }

  /**
   * Returns a list of user agents that should get redirected.
   *
   * Uses userAgents parameter configured in filters.yml but may be overwritten
   *
   * @return array
   */
  protected function getUserAgents()
  {
    return $this-&gt;getParameter(&#039;userAgents&#039;, array());
  }

  /**
   * Returns the url to which the matching user agents should get redirected.
   *
   * Uses redirectUrl parameter configured in filters.yml but may be overwritten
   *
   * @return string
   */
  protected function getRedirectUrl()
  {
  	return $this-&gt;getParameter(&#039;redirectUrl&#039;);
  }

  /**
   * Returns a list of request parameter names that should stop/suppress redirection
   *
   * Uses stopParameters parameter configured in filters.yml but may be overwritten
   *
   * @return array
   */
  protected function getStopParameters()
  {
  	return $this-&gt;getParameter(&#039;stopParameters&#039;, array());
  }
}
</pre>
<p>Then I created a more specialized filter called redirectUserAgentMobileFilter which extends redirectUserAgenFilter and contains a ready-to-use pre-configured list of known mobile user agent strings.</p>
<p>Additionally it can be customized to convey additional information to the redirect target url. In my case it &#8220;translates&#8221; the requested url into an id and appends ?id=[ID] to the configured redirectUrl.</p>
<p>Integration into your application vai filters.yml:</p>
<pre class="brush: php">
redirectUserAgentMobile:
  class:  redirectUserAgentMobileFilter
  enabled: on
  param:
    redirectUrl: http://mobile.yourdomain.com?id=%s
    #    userAgents:
    #     - additional
    #     - user agent
    #     - strings
</pre>
<p>redirectUserAgentMobileFilter.class.php</p>
<pre class="brush: php">
&lt; ?php
/**
 * Redirect request if it was issued by one of the configured user agents
 * params:
 *  - array userAgents
 *  - string redirectUrl (may contain a %s sprintf placeholder)
 *
  * Example filters.yml integration:
 *
 * &lt;pre&gt;
redirectUserAgentMobile:
  class:  redirectUserAgentMobileFilter
  enabled: on
  param:
    redirectUrl: http://mobile.yourdomain.com?id=%s
    userAgents:
     - additional
     - user agent
     - strings

 *
 */
class redirectUserAgentMobileFilter extends redirectUserAgentFilter
{

	/**
	 * Pre-configured list of mobile user agents.
	 * May be extend using userAgents option in filters.yml
	 *
	 * @var array
	 */
	protected $userAgents = array(&quot;240x320&quot;,&quot;android&quot;,&quot;benq&quot;,&quot;blackberry&quot;,&quot;configuration/cldc&quot;,&quot;ipod&quot;,&quot;iphone&quot;,&quot;midp&quot;,&quot;mda&quot;,&quot;mmp/&quot;,&quot;mot-&quot;,&quot;netfront&quot;,&quot;nokia&quot;,&quot;opera mini&quot;,&quot;palmos&quot;,&quot;palmsource&quot;,&quot;panasonic&quot;,&quot;philips&quot;,&quot;phone&quot;,&quot;pocket pc&quot;,&quot;portable&quot;,&quot;portalmmm&quot;,&quot;sagem&quot;,&quot;samsung&quot;,&quot;sda&quot;,&quot;sgh-&quot;,&quot;sharp&quot;,&quot;sie-&quot;,&quot;sonyericsson&quot;,&quot;symbian&quot;,&quot;up.browser&quot;,&quot;vodafone&quot;,&quot;web&#039;n&#039;walk&quot;,&quot;windows ce&quot;,&quot;xda&quot;);

  /**
   * Get the id of the requested content to include it in the redirectUrl
   *
   * @return int | null
   */
  protected function getContentId()
  {
    $contentAsset = $this-&gt;getContext()-&gt;getRequest()-&gt;getSelectedContentAsset();
    if ($contentAsset instanceof contentAsset &amp;&amp; $contentAsset-&gt;isVisible()) {
      return $contentAsset-&gt;remote_id;
    }

    return null;
  }

  /**
   * Get the id of the rubric (first level navigation node) belonging to the requested content to include it in the redirectUrl
   *
   * @return int | null
   */
  protected function getRubricId()
  {
    $contentAsset = $this-&gt;getContext()-&gt;getRequest()-&gt;getSelectedContentAsset();
    if ($contentAsset instanceof contentAsset) {
      $node = $contentAsset-&gt;getNode();
      if ($node) {
      	$rubric = $node-&gt;getRubricNode();
      	if ($rubric) {
      		$rubricContentAsset = $rubric-&gt;getCorrespondingContentAsset();
      		if ($rubricContentAsset) {
      			return $rubricContentAsset-&gt;remote_id;
      		}
      	}
      }
    }

    return null;
  }

  /**
   * Get the status code for the current request
   * Possible return values [and explanations]:
   * - 302 [Found]
   * - 404 [Not Found]
   * - 406 [Not Acceptable - not visible in mobile version]
   *
   * @return int
   */
  protected function getStatusCode()
  {
    $contentAsset = $this-&gt;getContext()-&gt;getRequest()-&gt;getSelectedContentAsset();
    if (!$contentAsset instanceof contentAsset ||
        !$contentAsset-&gt;isVisible()) {
    	return 404; //Not Found
    }
    elseif (!$contentAsset-&gt;getCMSMeta(&#039;mobile&#039;, true)) {
      return 406; //Not Acceptable
    }

    return 302; //found
  }

	/**
   * Override method
   * @see filter/redirectUserAgentFilter#getRedirectUrl()
   *
   * - include target ID in redirectUrl
   *
   * @return string | null
   */
  protected function getRedirectUrl()
  {
  	$contentId = $this-&gt;getContentId();
  	$rubricId = $this-&gt;getRubricId();
  	$statusCode = $this-&gt;getStatusCode();
		return sprintf(parent::getRedirectUrl(), $contentId, $rubricId, $statusCode);
  }

  /**
   * Override method
   * @see filter/redirectUserAgentFilter#getUserAgents()
   *
   * - merge pre-configuerd mobile user agents {@see $this-&gt;userAgents} with those configured using
   *   userAgents option in filters.yml
   */
  protected function getUserAgents()
  {
  	return array_merge(parent::getUserAgents(), $this-&gt;userAgents);
  }
}
</pre>
<p></a></head></useragents></script></head></p>
]]></content:encoded>
			<wfw:commentRss>http://jnotes.jonasfischer.net/2009/11/symfony-filter-redirect-useragent/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Important correction of the &#8220;More with symfony book&#8221;&#8217;s &#8220;Developing for Facebook&#8221; chapter</title>
		<link>http://jnotes.jonasfischer.net/2009/11/important-correction-of-the-more-with-symfony-books-developing-for-facebook-chapter/</link>
		<comments>http://jnotes.jonasfischer.net/2009/11/important-correction-of-the-more-with-symfony-books-developing-for-facebook-chapter/#comments</comments>
		<pubDate>Sat, 14 Nov 2009 16:26:46 +0000</pubDate>
		<dc:creator>Jonas Fischer</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://jnotes.jonasfischer.net/?p=282</guid>
		<description><![CDATA[I planned to use the sfFacebookConnectPlugin to pre-fill user a user profile form in one of my upcoming projects. At first I thought this would be no problem because the &#8220;More with Symfony&#8221; Book states:
&#8220;The terms of use of Facebook Connect clearly remind that one should not store any personal information about the user without [...]]]></description>
			<content:encoded><![CDATA[<p>I planned to use the sfFacebookConnectPlugin to pre-fill user a user profile form in one of my upcoming projects. At first I thought this would be no problem because the &#8220;More with Symfony&#8221; Book states:<br />
&#8220;The terms of use of Facebook Connect clearly remind that one should not store any personal information about the user without the user explicitly agreeing about it, but <strong>the information provided can be used to fill forms and ask for confirmation in a click</strong>. Additionally, the website can rely on public information like name and profile picture without needing to store them.&#8221; (<a href="http://www.symfony-project.org/more-with-symfony/1_4/en/12-Developing-for-Facebook" target="_blank">http://www.symfony-project.org/more-with-symfony/1_4/en/12-Developing-for-Facebook</a>)</p>
<p>Unfortunately (or fortunately, because I had not invested too much time in developing the feature, yet), as a digged through the facebook api documentation, I read that this is no longer true:<br />
&#8220;For example, we clarified that user data you get from the API is always subject to the  restrictions on storage (so <strong>you shouldn&#8217;t pre-fill data in a form for users to submit to your application</strong> if you plan to store that data; users should enter that information directly)&#8221; (see<a href="http://developers.facebook.com/news.php?blog=1&amp;story=234" target="_blank"> http://developers.facebook.com/news.php?blog=1&amp;story=234</a> or <a href="http://www.facebook.com/help/?page=431#!/help.php?page=888" target="_blank">http://www.facebook.com/help/?page=431#!/help.php?page=888</a>)</p>
]]></content:encoded>
			<wfw:commentRss>http://jnotes.jonasfischer.net/2009/11/important-correction-of-the-more-with-symfony-books-developing-for-facebook-chapter/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PHP&#8217;s ZipArchive Class has problems with too many (1012+) files</title>
		<link>http://jnotes.jonasfischer.net/2009/11/phps-ziparchive-class-has-problems-with-too-many-1012-files/</link>
		<comments>http://jnotes.jonasfischer.net/2009/11/phps-ziparchive-class-has-problems-with-too-many-1012-files/#comments</comments>
		<pubDate>Wed, 11 Nov 2009 21:40:54 +0000</pubDate>
		<dc:creator>Jonas Fischer</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://jnotes.jonasfischer.net/?p=252</guid>
		<description><![CDATA[ZipArchive seems to fail when adding the 1012th file.
I tested this with two different file sized and in bith cases ZipArchive::addFile() fails when adding the 1012th file. Strange.
Then I tested the same with the ZipArchiveImproved Class proposed by Farzad Ghanei (http://de.php.net/manual/de/function.ziparchive-addfile.php#88266) without problems.
I stopped testing after 5000 files without problems.
I tested this on two different [...]]]></description>
			<content:encoded><![CDATA[<p>ZipArchive seems to fail when adding the 1012th file.<br />
I tested this with two different file sized and in bith cases ZipArchive::addFile() fails when adding the 1012th file. Strange.</p>
<p>Then I tested the same with the ZipArchiveImproved Class proposed by Farzad Ghanei (http://de.php.net/manual/de/function.ziparchive-addfile.php#88266) without problems.<br />
I stopped testing after 5000 files without problems.</p>
<p>I tested this on two different machines both running PHP 5.2.6-3ubuntu4.2 with Suhosin-Patch 0.9.6.2</p>
<p>If you need to create a zip file and let the user stream its content while your script is still creating the archive, you should have a look at <a href="If you need to create a zip file and let the user stream it while creation is ongoing, http://pablotron.org/software/zipstream-php/ might be the right choice for you." target="_blank">http://pablotron.org/software/zipstream-php/</a>.<br />
<span id="more-252"></span><br />
ZipArchiveTest.php:</p>
<pre class="brush: php">
&lt; ?php
ini_set(&#039;memory_limit&#039;, &#039;64M&#039;);
set_time_limit(0);
require_once &#039;ZipArchiveImproved.class.php&#039;;

$zipFileNamePostfix = empty($_REQUEST[&#039;zipFileNamePostfix&#039;]) ? 0 : intval($_REQUEST[&#039;zipFileNamePostfix&#039;]);
$zipFileName = &quot;ZipArchiveTest$zipFileNamePostfix.zip&quot;;

$allowedTestFileNames = array(&#039;image.jpg&#039;, &#039;image_big.jpg&#039;);
$testFileName = empty($_REQUEST[&#039;testFileName&#039;]) ? $allowedTestFileNames[0] : $_REQUEST[&#039;testFileName&#039;];
if (!in_array($testFileName, $allowedTestFileNames)) {
$testFileName = $allowedTestFileNames[0];
}
$limit = empty($_REQUEST[&#039;limit&#039;]) ? 1 : intval($_REQUEST[&#039;limit&#039;]);
$limit = min(10000, $limit);

$allowedZipArchiveClassNames = array(&#039;ZipArchive&#039;, &#039;ZipArchiveImproved&#039;);
$zipArchiveClassName = empty($_REQUEST[&#039;zipArchiveClassName&#039;]) ? $allowedZipArchiveClassNames[0] : $_REQUEST[&#039;zipArchiveClassName&#039;];
if (!in_array($zipArchiveClassName, $allowedZipArchiveClassNames)) {
$zipArchiveClassName = $allowedZipArchiveClassNames[0];
}
?&gt;
Test adding &lt; ?php echo $limit; ?&gt; times &lt; ?php echo $testFileName; ?&gt; to &lt;a href=&quot;&lt;?php echo $zipFileName; ?&gt;&quot;&gt;&lt; ?php echo $zipFileName; ?&gt;&lt;/a&gt;&lt;br /&gt;
Test Filesize: &lt; ?php echo filesize($testFileName); ?&gt; Bytes&lt;br /&gt;
ZipArchiveClassName: &lt; ?php echo $zipArchiveClassName; ?&gt;&lt;br /&gt;

&lt; ?php
@unlink($zipFileName);
$zip = new $zipArchiveClassName;
$res = $zip-&gt;open($zipFileName, ZipArchive::CREATE);
if ($res === TRUE) {
for ($i=0; $i&lt; $limit; $i++) {
if (!$zip-&gt;addFile($testFileName, &quot;$i.$testFileName&quot;)) {
echo &quot;Failed to add {$i}th $testFileName&lt;br /&gt;\n&quot;;
break;
};
}
if ($zip-&gt;close()) {
echo &quot;Successfully closed zip resource&lt;br /&gt;\n&quot;;
echo &quot;Zip Filesize after test: &quot;.filesize($zipFileName).&quot;&lt;br /&gt;\n&quot;;
} else {
echo &quot;Failed to close zip resource&lt;br /&gt;\n&quot;;
};
}
</pre>
<p>ZipArchiveImproved.class.php</p>
<pre class="brush: php">
&lt; ?php
/**
* ZipArchiveImproved extends ZipArchive to add some information about the zip file and some functionality.
* See http://de.php.net/manual/de/function.ziparchive-addfile.php#88266
*
*
* @author Farzad Ghanei
* @uses ZipArchive
* @version 1.0.0 2009-01-18
*/

class ZipArchiveImproved extends ZipArchive {
protected $_archiveFileName = null;
protected $_newAddedFilesCounter = 0;
protected $_newAddedFilesSize = 100;

/**
* returns the name of the archive file.
*
* @return string
*/
public function getArchiveFileName() {
return $this-&gt;_archiveFileName;
}

/**
* returns the number of files that are going to be added to ZIP
* without reopenning the stream to file.
*
* @return int
*/
public function getNewAddedFilesSize() {
return $this-&gt;_newAddedFilesSize;
}

/**
* sets the number of files that are going to be added to ZIP
* without reopenning the stream to file. if no size is specified, default is 100.
*
* @param int
* @return ZipArchiveImproved self reference
*/
public function setNewlAddedFilesSize($size=100) {
if ( empty($size) || !is_int($size) || $size &lt; 1) {
$size = 100;
}
$this-&gt;_newAddedFilesSize = $size;
return $this;
}

/**
* opens a stream to a ZIP archive file. calls the ZipArchive::open() internally.
* overwrites ZipArchive::open() to add the archiveFileName functionality.
*
* @param string $fileName
* @param int $flags
* return mixed
*/
public function open($fileName, $flags) {
$this-&gt;_archiveFileName = $fileName;
$this-&gt;_newAddedFilesCounter = 0;
return parent::open($fileName,$flags);
}

/**
* closes the stream to ZIP archive file. calls the ZipArchive::close() internally.
* overwrites ZipArchive::close() to add the archiveFileName functionality.
*
* @return bool
*/
public function close() {
$this-&gt;_archiveFileName = null;
$this-&gt;_newAddedFilesCounter = 0;
return parent::close();
}

/**
* closes the connection to ZIP file and openes the connection again.
*
* @return bool
*/
public function reopen() {
$archiveFileName = $this-&gt;_archiveFileName;
if ( !$this-&gt;close() ) {
return false;
}
return $this-&gt;open($archiveFileName,self::CREATE);
}

/**
* adds a file to a ZIP archive from the given path. calls the ZipArchive::addFile() internally.
* overwrites ZipArchive::addFile() to handle maximum file connections in operating systems.
*
* @param string $fileName the path to file to be added to archive
* @param string [optional] $localname the name of the file in the ZIP archive
* @return bool
*/
public function addFile( $fileName ) {
if ($this-&gt;_newAddedFilesCounter &gt;= $this-&gt;_newAddedFilesSize) {
$this-&gt;reopen();
}
if ( func_num_args() &gt; 1 ) {
$flags = func_get_arg(1);
$added = parent::addFile($fileName,$flags);
if ($added) {
$this-&gt;_newAddedFilesCounter++;
}
return $added;
}
$added = parent::addFile($fileName);
if ($added) {
$this-&gt;_newAddedFilesCounter++;
}
return $added;
} // public function addFile()
}
</pre>
<h2>Detailed Test Report:</h2>
<h3>ZipArchive Class Tests</h3>
<h4>image.jpg Tests</h4>
<p>ZipArchiveTest.php?limit=1012&amp;testFileName=image.jpg<br />
Test adding 1012 times image.jpg to ZipArchiveTest0.zip<br />
Test Filesize: 4370 Bytes<br />
ZipArchiveClassName: ZipArchive<br />
Successfully closed zip resource<br />
Zip Filesize after test: 4376726<br />
&#8212;<br />
ZipArchiveTest.php?limit=1013&amp;testFileName=image.jpg<br />
Test adding 1013 times image.jpg to ZipArchiveTest0.zip<br />
Test Filesize: 4370 Bytes<br />
ZipArchiveClassName: ZipArchive<br />
Failed to close zip resource</p>
<h4>image_big.jpg Tests</h4>
<p>ZipArchiveTest.php?limit=1012&amp;testFileName=image_big.jpg<br />
Test adding 1012 times image_big.jpg to ZipArchiveTest0.zip<br />
Test Filesize: 27673 Bytes<br />
ZipArchiveClassName: ZipArchive<br />
Successfully closed zip resource<br />
Zip Filesize after test: 27837922<br />
&#8212;<br />
ZipArchiveTest.php?limit=1013&amp;testFileName=image_big.jpg<br />
Test adding 1013 times image_big.jpg to ZipArchiveTest0.zip<br />
Test Filesize: 27673 Bytes<br />
ZipArchiveClassName: ZipArchive<br />
Failed to close zip resource</p>
<h2>ZipArchiveImproved Class Tests</h2>
<p>ZipArchiveTest.php?limit=1014&amp;testFileName=image_big.jpg&amp;zipArchiveClassName=ZipArchiveImproved<br />
Test adding 1014 times image_big.jpg to ZipArchiveTest0.zip<br />
Test Filesize: 27673 Bytes<br />
ZipArchiveClassName: ZipArchiveImproved<br />
Successfully closed zip resource<br />
Zip Filesize after test: 27892942</p>
]]></content:encoded>
			<wfw:commentRss>http://jnotes.jonasfischer.net/2009/11/phps-ziparchive-class-has-problems-with-too-many-1012-files/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

