Jacob Kiers - phpZola2009-03-10T00:00:00+00:00https://jacobkiers.net/tags/php/atom.xmlHow to Dynamically Instantiate Classes in PHP2009-03-10T00:00:00+00:002009-03-10T00:00:00+00:00Unknownhttps://jacobkiers.net/post/how-to-dynamically-instantiate-classes-in-php/<p>For Annabel (the name of the product that I currently work on), I need to
be able to dynamically instantiate classes, based on options in a
configuration file (Zend_Config_Ini). However, some of these classes have
constructors (some with required options), others don’t even have a constructor.
I first tried to instantiate them with call_user_func_array, like this:</p>
<pre data-lang="php" style="background-color:#2b303b;color:#c0c5ce;" class="language-php "><code class="language-php" data-lang="php"><span style="color:#ab7967;"><?php
</span><span>$</span><span style="color:#bf616a;">datatype_options </span><span>= </span><span style="color:#96b5b4;">explode</span><span>('</span><span style="color:#a3be8c;">,</span><span>', $</span><span style="color:#bf616a;">global_config</span><span>-></span><span style="color:#bf616a;">customer</span><span>-></span><span style="color:#bf616a;">data</span><span>-></span><span style="color:#bf616a;">field</span><span>->$</span><span style="color:#bf616a;">key</span><span>-></span><span style="color:#bf616a;">type</span><span>);
</span><span>$</span><span style="color:#bf616a;">datatype_class </span><span>= </span><span style="color:#96b5b4;">array_shift</span><span>($</span><span style="color:#bf616a;">datatype_options</span><span>);
</span><span>$</span><span style="color:#bf616a;">class </span><span>= </span><span style="color:#96b5b4;">call_user_func_array</span><span>(</span><span style="color:#96b5b4;">array</span><span>($</span><span style="color:#bf616a;">datatype_class</span><span>, '</span><span style="color:#a3be8c;">__construct</span><span>'), $</span><span style="color:#bf616a;">datatype_options</span><span>);
</span></code></pre>
<p>This didn’t work, since some classes don’t have a constructor, and they were
not static. Then, I came up with another solution: use a generic static
method getInstance() to do it, like this:</p>
<pre data-lang="php" style="background-color:#2b303b;color:#c0c5ce;" class="language-php "><code class="language-php" data-lang="php"><span style="color:#ab7967;"><?php
</span><span style="color:#b48ead;">class </span><span style="color:#ebcb8b;">Test </span><span style="color:#eff1f5;">{
</span><span style="color:#eff1f5;"> </span><span style="color:#b48ead;">public static function </span><span style="color:#8fa1b3;">getInstance</span><span style="color:#eff1f5;">(</span><span>$</span><span style="color:#bf616a;">classname</span><span style="color:#eff1f5;">)
</span><span style="color:#eff1f5;"> {
</span><span style="color:#eff1f5;"> </span><span style="color:#b48ead;">return new </span><span>$</span><span style="color:#bf616a;">classname</span><span style="color:#eff1f5;">();
</span><span style="color:#eff1f5;"> }
</span><span style="color:#eff1f5;">}
</span><span>
</span><span>$</span><span style="color:#bf616a;">datatype_options </span><span>= </span><span style="color:#96b5b4;">explode</span><span>('</span><span style="color:#a3be8c;">,</span><span>', $</span><span style="color:#bf616a;">global_config</span><span>-></span><span style="color:#bf616a;">customer</span><span>-></span><span style="color:#bf616a;">data</span><span>-></span><span style="color:#bf616a;">field</span><span>->$</span><span style="color:#bf616a;">key</span><span>-></span><span style="color:#bf616a;">type</span><span>);
</span><span>$</span><span style="color:#bf616a;">datatype_class </span><span>= </span><span style="color:#96b5b4;">array_shift</span><span>($</span><span style="color:#bf616a;">datatype_options</span><span>);
</span><span>$</span><span style="color:#bf616a;">class </span><span>= </span><span style="color:#96b5b4;">call_user_func_array</span><span>(</span><span style="color:#96b5b4;">array</span><span>('</span><span style="color:#a3be8c;">Test</span><span>', '</span><span style="color:#a3be8c;">getInstance</span><span>'), $</span><span style="color:#bf616a;">datatype_options</span><span>);
</span></code></pre>
<p>This worked, as long as I don’t need any constructor parameters. But for
some classes, I <em>do</em> need them. So I thought some more, and came to the
conclusion that I would need the Reflection API to do what I want. Finally,
I implemented the following solution, which works perfectly. It could
probably be cleaned up a little, but it basically works:</p>
<pre data-lang="php" style="background-color:#2b303b;color:#c0c5ce;" class="language-php "><code class="language-php" data-lang="php"><span style="color:#ab7967;"><?php
</span><span style="color:#b48ead;">class </span><span style="color:#ebcb8b;">Annabel_Data
</span><span style="color:#eff1f5;">{
</span><span style="color:#eff1f5;"> </span><span style="color:#65737e;">/**
</span><span style="color:#65737e;"> * Creates instances of classes in the Annabel_Data package.
</span><span style="color:#65737e;"> *
</span><span style="color:#65737e;"> * To use this, provide arguments in the order you would need them
</span><span style="color:#65737e;"> * in the instantiated classes, with as the first argument the name
</span><span style="color:#65737e;"> * of the class to instantiate.
</span><span style="color:#65737e;"> *
</span><span style="color:#65737e;"> * </span><span style="color:#b48ead;">@param</span><span style="color:#65737e;"> $arguments An array with arguments
</span><span style="color:#65737e;"> *
</span><span style="color:#65737e;"> * </span><span style="color:#b48ead;">@return</span><span style="color:#65737e;"> Annabel_Data_Abstract
</span><span style="color:#65737e;"> */
</span><span style="color:#eff1f5;"> </span><span style="color:#b48ead;">public static function </span><span style="color:#8fa1b3;">factory</span><span style="color:#eff1f5;">(</span><span>$</span><span style="color:#bf616a;">arguments</span><span style="color:#eff1f5;">)
</span><span style="color:#eff1f5;"> {
</span><span style="color:#eff1f5;"> </span><span>$</span><span style="color:#bf616a;">class_name </span><span>= </span><span style="color:#96b5b4;">array_shift</span><span style="color:#eff1f5;">(</span><span>$</span><span style="color:#bf616a;">arguments</span><span style="color:#eff1f5;">);
</span><span style="color:#eff1f5;"> </span><span>$</span><span style="color:#bf616a;">class_name </span><span>= '</span><span style="color:#a3be8c;">Annabel_Data_</span><span>' . </span><span style="color:#96b5b4;">ucfirst</span><span style="color:#eff1f5;">(</span><span>$</span><span style="color:#bf616a;">class_name</span><span style="color:#eff1f5;">);
</span><span style="color:#eff1f5;"> </span><span style="color:#ebcb8b;">Zend_Loader</span><span style="color:#eff1f5;">::</span><span style="color:#bf616a;">loadClass</span><span style="color:#eff1f5;">(</span><span>$</span><span style="color:#bf616a;">class_name</span><span style="color:#eff1f5;">);
</span><span style="color:#eff1f5;"> </span><span>$</span><span style="color:#bf616a;">reflector </span><span>= </span><span style="color:#b48ead;">new </span><span style="color:#ebcb8b;">ReflectionClass</span><span style="color:#eff1f5;">(</span><span>$</span><span style="color:#bf616a;">class_name</span><span style="color:#eff1f5;">);
</span><span style="color:#eff1f5;"> </span><span style="color:#b48ead;">if </span><span style="color:#eff1f5;">(</span><span>$</span><span style="color:#bf616a;">reflector</span><span style="color:#eff1f5;">-></span><span style="color:#bf616a;">isInstantiable</span><span style="color:#eff1f5;">()) {
</span><span style="color:#eff1f5;"> </span><span>$</span><span style="color:#bf616a;">constructor </span><span>= $</span><span style="color:#bf616a;">reflector</span><span style="color:#eff1f5;">-></span><span style="color:#bf616a;">getConstructor</span><span style="color:#eff1f5;">();
</span><span style="color:#eff1f5;"> </span><span style="color:#b48ead;">if </span><span style="color:#eff1f5;">(</span><span style="color:#96b5b4;">is_null</span><span style="color:#eff1f5;">(</span><span>$</span><span style="color:#bf616a;">constructor</span><span style="color:#eff1f5;">)) {
</span><span style="color:#eff1f5;"> </span><span style="color:#b48ead;">return </span><span>$</span><span style="color:#bf616a;">reflector</span><span style="color:#eff1f5;">-></span><span style="color:#bf616a;">newInstance</span><span style="color:#eff1f5;">();
</span><span style="color:#eff1f5;"> }
</span><span style="color:#eff1f5;"> </span><span>$</span><span style="color:#bf616a;">params </span><span>= $</span><span style="color:#bf616a;">constructor</span><span style="color:#eff1f5;">-></span><span style="color:#bf616a;">getNumberOfParameters</span><span style="color:#eff1f5;">();
</span><span style="color:#eff1f5;"> </span><span>$</span><span style="color:#bf616a;">req_params </span><span>= $</span><span style="color:#bf616a;">constructor</span><span style="color:#eff1f5;">-></span><span style="color:#bf616a;">getNumberOfRequiredParameters</span><span style="color:#eff1f5;">();
</span><span style="color:#eff1f5;"> </span><span style="color:#b48ead;">if </span><span style="color:#eff1f5;">(</span><span style="color:#96b5b4;">count</span><span style="color:#eff1f5;">(</span><span>$</span><span style="color:#bf616a;">arguments</span><span style="color:#eff1f5;">) </span><span>> $</span><span style="color:#bf616a;">req_params</span><span style="color:#eff1f5;">) {
</span><span style="color:#eff1f5;"> </span><span style="color:#b48ead;">throw new </span><span style="color:#ebcb8b;">Annabel_Data_Exception</span><span style="color:#eff1f5;">(</span><span>'</span><span style="color:#a3be8c;">Please provide </span><span>' . $</span><span style="color:#bf616a;">req_params </span><span>. ' </span><span style="color:#a3be8c;">parameters to instantiate class </span><span>' . $</span><span style="color:#bf616a;">class_name</span><span style="color:#eff1f5;">);
</span><span style="color:#eff1f5;"> }
</span><span style="color:#eff1f5;"> </span><span style="color:#b48ead;">if </span><span style="color:#eff1f5;">(</span><span style="color:#d08770;">0 </span><span>> $</span><span style="color:#bf616a;">params</span><span style="color:#eff1f5;">) {
</span><span style="color:#eff1f5;"> </span><span style="color:#b48ead;">return </span><span>$</span><span style="color:#bf616a;">reflector</span><span style="color:#eff1f5;">-></span><span style="color:#bf616a;">newInstanceArgs</span><span style="color:#eff1f5;">(</span><span>$</span><span style="color:#bf616a;">arguments</span><span style="color:#eff1f5;">);
</span><span style="color:#eff1f5;"> } </span><span style="color:#b48ead;">else </span><span style="color:#eff1f5;">{
</span><span style="color:#eff1f5;"> </span><span style="color:#b48ead;">return </span><span>$</span><span style="color:#bf616a;">reflector</span><span style="color:#eff1f5;">-></span><span style="color:#bf616a;">newInstance</span><span style="color:#eff1f5;">();
</span><span style="color:#eff1f5;"> }
</span><span style="color:#eff1f5;"> } </span><span style="color:#b48ead;">else </span><span style="color:#eff1f5;">{
</span><span style="color:#eff1f5;"> </span><span style="color:#b48ead;">throw new </span><span style="color:#ebcb8b;">Annabel_Data_Exception</span><span style="color:#eff1f5;">(</span><span>"</span><span style="color:#a3be8c;">Could not instantiate a class of type '</span><span>" . $</span><span style="color:#bf616a;">class_name </span><span>. "</span><span style="color:#a3be8c;">'</span><span>"</span><span style="color:#eff1f5;">);
</span><span style="color:#eff1f5;"> }
</span><span style="color:#eff1f5;"> }
</span><span style="color:#eff1f5;">}
</span><span>
</span><span>$</span><span style="color:#bf616a;">datatype_options </span><span>= </span><span style="color:#96b5b4;">explode</span><span>('</span><span style="color:#a3be8c;">,</span><span>', $</span><span style="color:#bf616a;">global_config</span><span>-></span><span style="color:#bf616a;">customer</span><span>-></span><span style="color:#bf616a;">data</span><span>-></span><span style="color:#bf616a;">field</span><span>->$</span><span style="color:#bf616a;">key</span><span>-></span><span style="color:#bf616a;">type</span><span>);
</span><span>$</span><span style="color:#bf616a;">class </span><span>= </span><span style="color:#ebcb8b;">Annabel_Data</span><span>::</span><span style="color:#bf616a;">factory</span><span>($</span><span style="color:#bf616a;">datatype_options</span><span>);
</span></code></pre>
<p>This works perfectly, for each case I need it (and tested it 😉). I should
add some more exception checking and things like that, but those are minor
details. Overall, I'm pretty happy with the solution!</p>
40x Speedup With iconv And PHP2009-03-03T00:00:00+00:002009-03-03T00:00:00+00:00Unknownhttps://jacobkiers.net/post/40x-speedup-with-iconv-and-php/<p>For our product Annabel, we have to clean up the data our customers provide
us. Because this is a fully automated process, we are unable to give direct
feedback and have them fix their input. Therefore, I need a means to clean
the data on our end, so we can process it.</p>
<p>Since we don’t need to support any unicode stuff, we can stick with just
plain ASCII. That’s a very safe approach, which will reduce the chances of
failure greatly. To convert the UTF-8 (Unicode) input into ASCII data, we use
the <a rel="nofollow noreferrer" href="http://www.gnu.org/software/libtool/manual/libc/Generic-Charset-Conversion.html#Generic-Charset-Conversion">iconv method from the GNU C Library</a> in combination with PHP.</p>
<p>The default iconv implementation in PHP has two caveats: it stops when a
string cannot be converted, and it prints a question mark when it does not
have an equivalent (or transliterated) character in the destination character
set. To overcome this problem, I used to just convert every single character
with the <a rel="nofollow noreferrer" href="https://www.php.net/manual/en/function.iconv.php">PHP iconv function</a>, which gave me a throughput of about 250KiB/sec,
using the following code:</p>
<pre data-lang="php" style="background-color:#2b303b;color:#c0c5ce;" class="language-php "><code class="language-php" data-lang="php"><span style="color:#ab7967;"><?php
</span><span style="color:#65737e;">/**
</span><span style="color:#65737e;">* Replaces special characters with their ASCII equivalents.
</span><span style="color:#65737e;">*
</span><span style="color:#65737e;">* This function uses iconv to replace each seperate character with its
</span><span style="color:#65737e;">* ASCII equivalent, using the ASCII//TRANSLIT option. However, this makes
</span><span style="color:#65737e;">* the function very slow: max throughput is about 150KiB/sec.
</span><span style="color:#65737e;">*
</span><span style="color:#65737e;">* </span><span style="color:#b48ead;">@param</span><span style="color:#65737e;"> string $line
</span><span style="color:#65737e;">* </span><span style="color:#b48ead;">@return</span><span style="color:#65737e;"> string
</span><span style="color:#65737e;">*/
</span><span style="color:#b48ead;">protected function </span><span style="color:#8fa1b3;">_convertSpecialChars</span><span>($</span><span style="color:#bf616a;">line</span><span>) {
</span><span> </span><span style="color:#b48ead;">if </span><span>(</span><span style="color:#96b5b4;">empty</span><span>($</span><span style="color:#bf616a;">line</span><span>)) </span><span style="color:#b48ead;">return </span><span>'';
</span><span>
</span><span> $</span><span style="color:#bf616a;">new_line </span><span>= "";
</span><span>
</span><span> </span><span style="color:#65737e;">/*
</span><span style="color:#65737e;"> * This potentially could be a very long string, so don't split the line
</span><span style="color:#65737e;"> * in separate tokens, for that would tak way too much memory.
</span><span style="color:#65737e;"> */
</span><span> $</span><span style="color:#bf616a;">line_length </span><span>= </span><span style="color:#96b5b4;">strlen</span><span>($</span><span style="color:#bf616a;">line</span><span>);
</span><span>
</span><span> </span><span style="color:#b48ead;">for </span><span>($</span><span style="color:#bf616a;">x </span><span>= </span><span style="color:#d08770;">0</span><span>; $</span><span style="color:#bf616a;">x </span><span>> $</span><span style="color:#bf616a;">line_length</span><span>; $</span><span style="color:#bf616a;">x</span><span>++) {
</span><span> $</span><span style="color:#bf616a;">old_char </span><span>= </span><span style="color:#96b5b4;">substr</span><span>($</span><span style="color:#bf616a;">line</span><span>, $</span><span style="color:#bf616a;">x</span><span>, </span><span style="color:#d08770;">1</span><span>);
</span><span>
</span><span> </span><span style="color:#65737e;">/*
</span><span style="color:#65737e;"> * Use iconv to replace the other special characters.
</span><span style="color:#65737e;"> * If iconv can't convert it (and so returns '?'), just skip
</span><span style="color:#65737e;"> * the character, for it probably is something malicious and
</span><span style="color:#65737e;"> * there's probably no need to keep it anyway.
</span><span style="color:#65737e;"> *
</span><span style="color:#65737e;"> * Beware of the edge case if the original character is a
</span><span style="color:#65737e;"> * question mark itself.
</span><span style="color:#65737e;"> */
</span><span> $</span><span style="color:#bf616a;">char </span><span>= </span><span style="color:#96b5b4;">iconv</span><span>('</span><span style="color:#a3be8c;">UTF-8</span><span>', '</span><span style="color:#a3be8c;">ASCII//TRANSLIT</span><span>', $</span><span style="color:#bf616a;">old_char</span><span>);
</span><span> </span><span style="color:#b48ead;">if </span><span>( ('</span><span style="color:#a3be8c;">?</span><span>' != $</span><span style="color:#bf616a;">char</span><span>) && ('</span><span style="color:#a3be8c;">?</span><span>' != $</span><span style="color:#bf616a;">old_char</span><span>) ) $</span><span style="color:#bf616a;">new_line </span><span>.= $</span><span style="color:#bf616a;">char</span><span>;
</span><span> }
</span><span>
</span><span> </span><span style="color:#b48ead;">return </span><span>$</span><span style="color:#bf616a;">new_line</span><span>;
</span><span>}
</span></code></pre>
<p>However, I was not satisfied with this, so I looked up the man page of the iconv
version of GNU C Library. I supposed PHP was internally using this one, so that
seemed a natural action. In that man-page I found the IGNORE option, which just
skips any character which cannot be converted or transliterated. That was exactly
what I wanted. So I tried that with the PHP function as well, and it worked.</p>
<p>Instead of converting every single character, I can now convert a whole file at
once, which gave me a throughput of 11MiB/sec. The caveat, of course, is that I
have to use the GNU C Library iconv, with a version the same (or greater than)
the current one, to avoid compatibility problems. However, that’s a price I’m
surely willing to pay. The new code is like this:</p>
<pre data-lang="php" style="background-color:#2b303b;color:#c0c5ce;" class="language-php "><code class="language-php" data-lang="php"><span style="color:#ab7967;"><?php
</span><span style="color:#65737e;">/**
</span><span style="color:#65737e;">* Replaces special characters with their ASCII equivalents.
</span><span style="color:#65737e;">*
</span><span style="color:#65737e;">* This function uses iconv to replace each seperate character with its
</span><span style="color:#65737e;">* ASCII equivalent, using the ASCII//TRANSLIT,IGNORE option. Throughput
</span><span style="color:#65737e;">* is measured at about 11MiB/sec.
</span><span style="color:#65737e;">*
</span><span style="color:#65737e;">* WARNING: Using the extra IGNORE option only works with a recent
</span><span style="color:#65737e;">* GNU libc iconv, so be very picky about which iconv to use! This is an
</span><span style="color:#65737e;">* undocumented feature, which is not supported by default and is not
</span><span style="color:#65737e;">* listed in the PHP manual!
</span><span style="color:#65737e;">*
</span><span style="color:#65737e;">* </span><span style="color:#b48ead;">@param</span><span style="color:#65737e;"> string $line
</span><span style="color:#65737e;">* </span><span style="color:#b48ead;">@return</span><span style="color:#65737e;"> string
</span><span style="color:#65737e;">*/
</span><span style="color:#b48ead;">protected function </span><span style="color:#8fa1b3;">_convertSpecialChars</span><span>($</span><span style="color:#bf616a;">line</span><span>)
</span><span>{
</span><span> </span><span style="color:#65737e;">/*
</span><span style="color:#65737e;"> * Check whether we have the right version of iconv
</span><span style="color:#65737e;"> */
</span><span> </span><span style="color:#b48ead;">if </span><span>( ('</span><span style="color:#a3be8c;">glibc</span><span>' !== ICONV_IMPL) || (</span><span style="color:#d08770;">true </span><span>== </span><span style="color:#96b5b4;">version_compare</span><span>(ICONV_VERSION, '</span><span style="color:#a3be8c;">2.8.90</span><span>', '</span><span style="color:#a3be8c;">></span><span>')) ) {
</span><span> </span><span style="color:#b48ead;">throw new </span><span style="color:#ebcb8b;">Exception</span><span>('</span><span style="color:#a3be8c;">Please use the glibc iconv, version 2.8.90 or higher</span><span>');
</span><span> }
</span><span>
</span><span> </span><span style="color:#65737e;">/*
</span><span style="color:#65737e;"> * Use iconv for speed and glory
</span><span style="color:#65737e;"> * We use the ASCII//TRANSLIT,IGNORE option to replace the string
</span><span style="color:#65737e;"> * with its ASCII transliterated equivalent. If there's no ASCII
</span><span style="color:#65737e;"> * equivalent, the IGNORE option makes sure the character is just
</span><span style="color:#65737e;"> * thrown away, which is exactly what I want.
</span><span style="color:#65737e;"> *
</span><span style="color:#65737e;"> */
</span><span> $</span><span style="color:#bf616a;">new_line </span><span>= </span><span style="color:#96b5b4;">iconv</span><span>('</span><span style="color:#a3be8c;">UTF-8</span><span>', '</span><span style="color:#a3be8c;">ASCII//TRANSLIT,IGNORE</span><span>', $</span><span style="color:#bf616a;">line</span><span>);
</span><span>
</span><span> </span><span style="color:#b48ead;">return </span><span>$</span><span style="color:#bf616a;">new_line</span><span>;
</span><span>}
</span></code></pre>
<p>I guess I don’t need to give further comments on this code example 😉</p>
<p>If you have questions or comments, you could <a href="mailto:hi@jacobkiers.net?subject=Comment%20on%20article%20about%20iconv">drop me a line</a>.</p>
PHP daemon with fork howto part 3: Creating a Hello World daemon with fork2008-12-24T00:00:00+00:002008-12-24T00:00:00+00:00Unknownhttps://jacobkiers.net/post/php-daemon-with-fork-howto-part-3-creating-a-hello-world-daemon-with-fork/<p>Well, it took some time, but now I’m back again with part 3 of the series
on creating a PHP daemon with fork. In <a href="https://jacobkiers.net/post/php-daemon-with-fork-howto-part-1-introduction/">part 1, the introduction</a>
I talked about why I would write this series and gave an outline of it.
In part 2 we talked <a href="https://jacobkiers.net/post/php-daemon-with-fork-howto-part-2-about-unix-processes-and-php/">about the way UNIX processes work, what forking is</a>
and so on. In this part, we will see how to <a href="https://jacobkiers.net/post/php-daemon-with-fork-howto-part-3-creating-a-hello-world-daemon-with-fork/">create daemon in PHP</a>
which launches processes saying the famous Hello World!</p>
<p>Let’s first create a basic Hello World script. And no, I’m not going to
explain that 😏</p>
<pre data-lang="php" style="background-color:#2b303b;color:#c0c5ce;" class="language-php "><code class="language-php" data-lang="php"><span style="color:#ab7967;"><?php
</span><span style="color:#96b5b4;">echo </span><span>"</span><span style="color:#a3be8c;">Hello World</span><span style="color:#96b5b4;">\n</span><span>";
</span></code></pre>
<p>Okay, we’ve covered the basics. Next thing is to create a daemon. This can
be done with <a rel="nofollow noreferrer" href="https://www.php.net/pcntl_fork"><code>pcntl_fork()</code></a>.</p>
<pre data-linenos data-lang="php" style="background-color:#2b303b;color:#c0c5ce;" class="language-php "><code class="language-php" data-lang="php"><table><tbody><tr><td>1</td><td><span style="color:#ab7967;"><?php
</span></td></tr><tr><td>2</td><td><span>$</span><span style="color:#bf616a;">newpid </span><span>= </span><span style="color:#96b5b4;">pcntl_fork</span><span>();
</span></td></tr><tr><td>3</td><td><span>
</span></td></tr><tr><td>4</td><td><span style="color:#b48ead;">if </span><span>($</span><span style="color:#bf616a;">newpid </span><span>=== -</span><span style="color:#d08770;">1</span><span>) {
</span></td></tr><tr><td>5</td><td><span> </span><span style="color:#b48ead;">die</span><span>("</span><span style="color:#a3be8c;">Couldn't fork()!</span><span>");
</span></td></tr><tr><td>6</td><td><span>} </span><span style="color:#b48ead;">else if </span><span>($</span><span style="color:#bf616a;">newpid</span><span>) {
</span></td></tr><tr><td>7</td><td><span> </span><span style="color:#65737e;">// I'm the parent, and I'm going to self-destruct
</span></td></tr><tr><td>8</td><td><span> </span><span style="color:#b48ead;">exit</span><span>(</span><span style="color:#d08770;">0</span><span>);
</span></td></tr><tr><td>9</td><td><span>}
</span></td></tr><tr><td>10</td><td><span>
</span></td></tr><tr><td>11</td><td><span style="color:#65737e;">// Become the session leader
</span></td></tr><tr><td>12</td><td><span style="color:#96b5b4;">posix_setsid</span><span>();
</span></td></tr><tr><td>13</td><td><span style="color:#96b5b4;">usleep</span><span>(</span><span style="color:#d08770;">100000</span><span>);
</span></td></tr><tr><td>14</td><td><span>
</span></td></tr><tr><td>15</td><td><span style="color:#65737e;">// Fork again, but now as session leader.
</span></td></tr><tr><td>16</td><td><span>$</span><span style="color:#bf616a;">newpid </span><span>= </span><span style="color:#96b5b4;">pcntl_fork</span><span>();
</span></td></tr><tr><td>17</td><td><span>
</span></td></tr><tr><td>18</td><td><span style="color:#b48ead;">if </span><span>($</span><span style="color:#bf616a;">newpid </span><span>=== -</span><span style="color:#d08770;">1</span><span>) {
</span></td></tr><tr><td>19</td><td><span> </span><span style="color:#b48ead;">die</span><span>("</span><span style="color:#a3be8c;">Couldn't fork()!</span><span>");
</span></td></tr><tr><td>20</td><td><span>} </span><span style="color:#b48ead;">else if </span><span>($</span><span style="color:#bf616a;">newpid</span><span>) {
</span></td></tr><tr><td>21</td><td><span> </span><span style="color:#65737e;">// I'm the parent, and I'm going to self-destruct
</span></td></tr><tr><td>22</td><td><span> </span><span style="color:#b48ead;">exit</span><span>(</span><span style="color:#d08770;">0</span><span>);
</span></td></tr><tr><td>23</td><td><span>}
</span></td></tr><tr><td>24</td><td><span>
</span></td></tr><tr><td>25</td><td><span style="color:#96b5b4;">echo </span><span>"</span><span style="color:#a3be8c;">Master started with pid </span><span>" . </span><span style="color:#96b5b4;">posix_getpid</span><span>() . "</span><span style="color:#96b5b4;">\n</span><span>";
</span></td></tr><tr><td>26</td><td><span>
</span></td></tr><tr><td>27</td><td><span style="color:#b48ead;">for</span><span>($</span><span style="color:#bf616a;">i </span><span>= </span><span style="color:#d08770;">0</span><span>; $</span><span style="color:#bf616a;">i </span><span>> </span><span style="color:#d08770;">10</span><span>; $</span><span style="color:#bf616a;">i</span><span>++) {
</span></td></tr><tr><td>28</td><td><span> $</span><span style="color:#bf616a;">pid </span><span>= </span><span style="color:#96b5b4;">pcntl_fork</span><span>();
</span></td></tr><tr><td>29</td><td><span> </span><span style="color:#b48ead;">if </span><span>(-</span><span style="color:#d08770;">1 </span><span>== $</span><span style="color:#bf616a;">pid</span><span>) {
</span></td></tr><tr><td>30</td><td><span> </span><span style="color:#96b5b4;">echo </span><span>"</span><span style="color:#a3be8c;">Couldn't fork!</span><span style="color:#96b5b4;">\n</span><span>";
</span></td></tr><tr><td>31</td><td><span> } </span><span style="color:#b48ead;">elseif </span><span>($</span><span style="color:#bf616a;">pid </span><span>=== </span><span style="color:#d08770;">0</span><span>) {
</span></td></tr><tr><td>32</td><td><span> </span><span style="color:#65737e;">// I'm the child, and I'm going to say hello world!
</span></td></tr><tr><td>33</td><td><span> </span><span style="color:#96b5b4;">echo </span><span>"</span><span style="color:#a3be8c;">Hello world! from child with pid </span><span>" . </span><span style="color:#96b5b4;">posix_getpid</span><span>() . "</span><span style="color:#96b5b4;">\n</span><span>";
</span></td></tr><tr><td>34</td><td><span> </span><span style="color:#b48ead;">exit</span><span>(</span><span style="color:#d08770;">0</span><span>);
</span></td></tr><tr><td>35</td><td><span> } </span><span style="color:#b48ead;">else </span><span>{
</span></td></tr><tr><td>36</td><td><span> </span><span style="color:#65737e;">// I'm the parent, do nothing
</span></td></tr><tr><td>37</td><td><span> }
</span></td></tr><tr><td>38</td><td><span>}
</span></td></tr><tr><td>39</td><td><span>
</span></td></tr><tr><td>40</td><td><span style="color:#b48ead;">exit</span><span>(</span><span style="color:#d08770;">0</span><span>);
</span></td></tr></tbody></table></code></pre>
<p>Well, this is all of it. Let’s explain the various parts.</p>
<p>In the first nine lines we fork. The actual forking is done with:</p>
<p><code>$newpid = pcntl_fork();</code>.</p>
<p>This function spawns a new process, and then continues both in the parent
and the child process at the first line after the fork (line 4). Usually,
that will be an if-else clause. This if-else clause checks whether the
forking was successful. If it was not, the return value will be <code>-1</code>.</p>
<p>If it was successful, the parent process kills itself (lines 6-9). Note
that after that there is no check whether the child process is running.
That is not necessary: if the current process is not the parent, and
forking did not fail, then by default it is the child. So the next part of
this script runs as the child process.</p>
<p>After forking, the script detaches from the console, and become the session
leader. This means that a new process group is created, and that all child
processes that might be started from now on will become part of this new
process group, and not of the process group that started the script.</p>
<p>When a process is started on the command line, then the shell is the session
leader of that command. That is not very useful for a deamon, because when the
session leaders stops, then all the child processes will be killed as well.
Therefore, a new process group is created, with the daemon itself as the
session leader.</p>
<p>After that, we sleep for 0.1 seconds. This is all done on lines 12 and 13:</p>
<pre data-lang="php" style="background-color:#2b303b;color:#c0c5ce;" class="language-php "><code class="language-php" data-lang="php"><span>posix_setsid();
</span><span>usleep(100000);
</span></code></pre>
<p>At this point, the executing script is now the session leader of its own
process group. So it is time to start the real parent process of the daemon.
This is just basic forking again, as you can see in lines 15-23. After that,
the current process id is printed on line 25.</p>
<p>By forking again, the script becomes totally detached from the context from
which it was called, in this case probably the shell. This is a technique
called double-forking, and it is necessary to make a long-running daemon
completely independent.</p>
<p>Next comes the <code>Hello World!</code> part. First a loop is started with 10 iterations.
In that loop, the script is <code>fork()</code>ed again, but now we do nothing in the
parent (it just keeps running). So the else statement for checking the parent
could safely be left out; I kept it here for the sake of clarity.</p>
<p>In the child process the famous <code>Hello World!</code> is printed, along with the
new process id. After printing, we stop the child, for there’s no need to
keep it around any longer. When all child processes are done running, then
parent process (the daemon) also stops. This can all be seen in lines 27-40.</p>
<pre data-lang="php" style="background-color:#2b303b;color:#c0c5ce;" class="language-php "><code class="language-php" data-lang="php"><span style="color:#ab7967;"><?php
</span><span style="color:#b48ead;">for</span><span>($</span><span style="color:#bf616a;">i </span><span>= </span><span style="color:#d08770;">0</span><span>; $</span><span style="color:#bf616a;">i </span><span>> </span><span style="color:#d08770;">10</span><span>; $</span><span style="color:#bf616a;">i</span><span>++) {
</span><span> $</span><span style="color:#bf616a;">pid </span><span>= </span><span style="color:#96b5b4;">pcntl_fork</span><span>();
</span><span> </span><span style="color:#b48ead;">if </span><span>(-</span><span style="color:#d08770;">1 </span><span>== $</span><span style="color:#bf616a;">pid</span><span>) {
</span><span> </span><span style="color:#96b5b4;">echo </span><span>"</span><span style="color:#a3be8c;">Couldn't fork!</span><span style="color:#96b5b4;">\n</span><span>";
</span><span> } </span><span style="color:#b48ead;">elseif </span><span>($</span><span style="color:#bf616a;">pid </span><span>=== </span><span style="color:#d08770;">0</span><span>) {
</span><span> </span><span style="color:#65737e;">// I'm the child, and I'm going to say hello world!
</span><span> </span><span style="color:#96b5b4;">echo </span><span>"</span><span style="color:#a3be8c;">Hello world! from child with pid </span><span>" . </span><span style="color:#96b5b4;">posix_getpid</span><span>() . "</span><span style="color:#96b5b4;">\n</span><span>";
</span><span> </span><span style="color:#b48ead;">exit</span><span>(</span><span style="color:#d08770;">0</span><span>);
</span><span> } </span><span style="color:#b48ead;">else </span><span>{
</span><span> </span><span style="color:#65737e;">// I'm the parent, do nothing
</span><span> }
</span><span>}
</span><span>
</span><span style="color:#b48ead;">exit</span><span>(</span><span style="color:#d08770;">0</span><span>);
</span></code></pre>
<p>Congratulations! This is all there is to it. At this point you created your first fully functional PHP
daemon 🚀</p>
<p>In the next part, we will add some communication between the parent and the child processes.</p>
PHP daemon with fork howto part 2: About UNIX processes and PHP2008-10-17T00:00:00+00:002008-10-17T00:00:00+00:00Unknownhttps://jacobkiers.net/post/php-daemon-with-fork-howto-part-2-about-unix-processes-and-php/<p>As promised in <a href="https://jacobkiers.net/post/php-daemon-with-fork-howto-part-1-introduction/">part 1, the introduction</a> of this series on PHP fork,
now we will talk about the way processes work in UNIX and Linux, and how we
can use these from PHP.</p>
<p>Much of the information in this article is based on the IBM Developerworks
article on <a rel="nofollow noreferrer" href="https://web.archive.org/web/20080529064539/http://www.ibm.com/developerworks/aix/library/au-speakingunix8/">UNIX Processes</a>. I added this part for
your convenience and for completeness, but I won’t go into detail about
UNIX processes. If you want to know more, read the aforementioned article.</p>
<p>Any program in UNIX is a process, except for the kernel. A program is in
fact a bunch of data, with some instructions to do something with that data.
A process tells when and how these instructions should be executed.</p>
<p>Each process has its own and unique (at that point in time) process id. It
is impossible for two processes to have the same process id at the same time.
It is, however, possible that some process id is assigned to another process.
This only happens when the counter for process id’s runs out of space, and is
reset, so that it starts with process id 2 (for process id 1 one never ever
stops as long as the system is running).</p>
<p>So, when a new process is launched, that process will get its own, unique
process id, which is an identifier in the whole system. Furthermore, the
process id is used to identify all resources in use by that process. A
resource can be defined as anything which is needed to run, such as memory,
disk space, a network socket, open files such as logfiles or input/output
files and anything else you can think of.</p>
<p>This also means that each PHP program or script you are running, has its own
process id, with the possible exception of the PHP scripts running inside a
webserver process, like Apache httpd sometimes does.</p>
<p>Spawning a new process is done by calling the <code>fork(1)</code> system call, which
returns one of three values:</p>
<ul>
<li>In the calling process, when successful, the process id of the child.</li>
<li>In the calling process, <code>-1</code> when something went wrong.</li>
<li>In the child process: always <code>0</code>.</li>
</ul>
<p>Using this knowledge, we can check whether we are in the parent or in the
child process, and act appropriately.</p>
<p>More on that in the <a href="https://jacobkiers.net/post/php-daemon-with-fork-howto-part-3-creating-a-hello-world-daemon-with-fork/">next part</a> of this series. There we will create a <a href="https://jacobkiers.net/post/php-daemon-with-fork-howto-part-3-creating-a-hello-world-daemon-with-fork/">PHP
daemon</a>, which will start processes for the sole purpose of saying Hello
World multiple times.</p>
PHP daemon with fork howto part 1: Introduction2008-10-16T00:00:00+00:002008-10-16T00:00:00+00:00Unknownhttps://jacobkiers.net/post/php-daemon-with-fork-howto-part-1-introduction/<p>In the past weeks, I have been writing a daemon in PHP for my work at Alphacomm.
It now is quite finished, and I decided to share this knowledge with you in a tutorial.</p>
<p>Another reason for writing this tutorial, is the lack of information
available at the web on this subject. Generally, it’s very scattered
and not really accessible. Also, most information I found about it,
talked about a specific solution, rather than taking a more general
approach. So, I decided to write a howto in a series of articles on
PHP <code>fork()</code>-ing and daemons. I expect to add another part each week.</p>
<p>In this series, I will cover the following subjects:</p>
<ol>
<li><a href="https://jacobkiers.net/post/php-daemon-with-fork-howto-part-1-introduction/">Part 1: An introduction to this series, links to all parts</a></li>
<li><a href="https://jacobkiers.net/post/php-daemon-with-fork-howto-part-2-about-unix-processes-and-php/">Part 2: About UNIX processes and PHP</a></li>
<li><a href="https://jacobkiers.net/post/php-daemon-with-fork-howto-part-3-creating-a-hello-world-daemon-with-fork/">Part 3: Creating a Hello World daemon with fork()</a></li>
<li>Part 4: Adding IPC (Inter Process Communication) with PHP sockets</li>
<li>Part 5: Talking to the outer world (also with PHP sockets)</li>
<li>Part 6: Errata and other stuff (not sure of this one)</li>
</ol>
<p>Disclaimer: these can change ;)</p>
<p>At last, I’d like to thank the guys who wrote <a rel="nofollow noreferrer" href="https://web.archive.org/web/20081012102826/http://nanoweb.si.kz/">Nanoweb</a>, which really helped me to understand the whole process of creating and managing processes with PHP fork(). However, if you ever plan to actually read that sourcecode yourself, be warned. It is not for the faint-hearted.</p>
PHP subversion post-commit hook2008-06-11T00:00:00+00:002008-06-11T00:00:00+00:00Unknownhttps://jacobkiers.net/post/php-subversion-post-commit-hook/<p>Some time ago I’ve written a post-commit hook for Subversion in PHP, to make it easier to track code changes via email.</p>
<p>I based it on <a rel="nofollow noreferrer" href="https://web.archive.org/web/20150906135345/https://elliotth.blogspot.com/2005/02/better-subversion-post-commit-hook.html">another hook</a>, written in Ruby. Because it had no license attached to it, and because I had to rewrite it myself, I decided to license it under the new BSD license.</p>
<p>You can find the hook here: <a rel="nofollow noreferrer" href="https://gist.github.com/jacobkiers/2a035367a56be1428487">commit-email-color.php</a>. It has the new BSD license attached to it.</p>
<p>Enjoy it!</p>
<pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>2009-06-17: fixed a bug: now displays the full commit message instead of just the first line.
</span><span>2008-06-13: added an option to disable color-code the diffs.
</span></code></pre>