<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <title>Plop in the Ocean</title>
    <link rel="alternate" type="text/html" href="http://pgregg.com/blog/" />
    <link rel="self" type="application/atom+xml" href="http://pgregg.com/blog/atom.xml" />
    <id>tag:pgregg.com,2009-02-03:/blog//2</id>
    <updated>2010-12-02T12:31:11Z</updated>
    
    <generator uri="http://www.sixapart.com/movabletype/">Movable Type 4.34-en</generator>

<entry>
    <title>Stage 2: http:BL with Apache2 mod_perl</title>
    <link rel="alternate" type="text/html" href="http://pgregg.com/blog/2010/12/stage-2-httpbl-with-apache2-mod-perl.html" />
    <id>tag:pgregg.com,2010:/blog//2.343</id>

    <published>2010-12-02T11:19:45Z</published>
    <updated>2010-12-02T12:31:11Z</updated>

    <summary><![CDATA[After my earlier post Referrer and Comment spammers are a PITA I came up with two mod_perl plugins to Apache and an "apache level" firewall.The reason for the apache-level firewall is two-fold.&nbsp; There is no direct way for the Apache user to manipulate an iptables chain (as it doesn't run as root), and second; I was not happy with suid root access or other forms of message passing to a daemon which would manipulate the firewall for me.Architecture is thus, in httpd.conf place the following two lines:PerlPreConnectionHandler PGREGG::httpBLBlockPerlLogHandler PGREGG::httpBLLogThe first tells apache to run the handler in my httpBLBlock.pm module...]]></summary>
    <author>
        <name>Paul Gregg</name>
        <uri>http://pgregg.com</uri>
    </author>
    
        <category term="PHP" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="Perl" scheme="http://www.sixapart.com/ns/types#category" />
    
    <category term="apache" label="apache" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="firewall" label="firewall" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="mod_perl" label="mod_perl" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="perl" label="perl" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="php" label="php" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en" xml:base="http://pgregg.com/blog/">
        <![CDATA[After my earlier post <a href="http://pgregg.com/blog/2010/11/referrer-and-comment-spammers-are-a-pita.html">Referrer and Comment spammers are a PITA</a> I came up with two mod_perl plugins to Apache and an "apache level" firewall.<br /><br />The reason for the apache-level firewall is two-fold.&nbsp; There is no direct way for the Apache user to manipulate an iptables chain (as it doesn't run as root), and second; I was not happy with suid root access or other forms of message passing to a daemon which would manipulate the firewall for me.<br /><br />Architecture is thus, in httpd.conf place the following two lines:<br /><blockquote>PerlPreConnectionHandler PGREGG::httpBLBlock<br />PerlLogHandler PGREGG::httpBLLog<br /></blockquote>The first tells apache to run the handler in my httpBLBlock.pm module when a connection is received (before the request has been sent by the client).&nbsp; In this handler, I am simply looking for a filename matching that IP in a directory that is writable by the apache user.&nbsp; The contents of the file are a SCORE:httpBL_answer:[LIST].&nbsp; Based on this, the module checks the mtime of the filename is in the last SCORE days, then the firewall is in effect. If so, we simply tell apache to drop the connection.&nbsp; If the file has expired, we delete the file.<br /><br />The second line is more interesting, and what creates the firewall filenames. In order to not impede the general speed of request handling, processing is performed in the Logging section of the Apache process. Our module is called by apache after the response has been sent, but before the access_log entry has been written.&nbsp; In our module we perform the <a href="http://www.projecthoneypot.org/httpbl_api.php">http:BL</a> API call and compute the above SCORE based upon the Threat* level and Age* of the API response. (* both Threat and Age are octets in the DNS lookup).&nbsp; We merely discount the Threat down to zero based on the Age (0-255) where an entry 255 days old reduces the SCORE to zero.<br />If the SCORE is larger than our trigger level (3) then we create the firewall filename, log the entry in our own httpbl.log and return Apache2::Const::FORBIDDEN.&nbsp; This causes Apache to not log the entry in the normal access_log.&nbsp; Otherwise, if all is ok, we return Apache2::Const::OK and Apache logs the hit as normal.<br /><br />I have a bit of code tidy up, restructure the config/firewall directory and pull some common code out to a shared module before I can release to the world.<br /><br />An interesting side effect to publishing the last story out through <a href="http://www.planet-php.net/">Planet PHP</a> and other news sources <i>along with the Project Honey Pot image</i> is that when browsers viewed those sources, they all asked for the image off my server. In several cases, these were known spammer, Comment spammer, and other abusers. My server then created the firewall entry blocking them before they were able to follow the links back to my server.<br />&nbsp;<br />I have been reading up more on <a href="http://perl.apache.org/docs/2.0/api/Apache2/Filter.html">Apache Bucket Brigades</a> in an attempt to allow the firewall filter to be placed immediately after the request has been received and allow a custom response to the browser. This may help an otherwise unsuspecting user if their machine had been trojaned. I don't mind admitting I'm thoroughly confused right now :)<br /><br />]]>
        
    </content>
</entry>

<entry>
    <title>Referrer and Comment spammers are a PITA.</title>
    <link rel="alternate" type="text/html" href="http://pgregg.com/blog/2010/11/referrer-and-comment-spammers-are-a-pita.html" />
    <id>tag:pgregg.com,2010:/blog//2.342</id>

    <published>2010-11-28T23:18:54Z</published>
    <updated>2010-11-28T23:45:53Z</updated>

    <summary><![CDATA[This shouldn't be news to anyone - but Referrer and Comment spammers are a real pain in the a*se.&nbsp; Polluting my web logs and making any meaningful log analysis problematic.So, I now have an itch to scratch and I'm going to do something about it. I would encourage you, the reader, to do something about it too.Firstly, get yourself over to Project Honey Pot and read up on the project.&nbsp; If you can, set up a Honey Pot or two yourself. Also be sure to read about the http:BL - this works along similar lines to the DNS blacklists used...]]></summary>
    <author>
        <name>Paul Gregg</name>
        <uri>http://pgregg.com</uri>
    </author>
    
        <category term="PHP" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="Perl" scheme="http://www.sixapart.com/ns/types#category" />
    
    <category term="httpbl" label="http:BL" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="spam" label="spam" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en" xml:base="http://pgregg.com/blog/">
        <![CDATA[This shouldn't be news to anyone - but Referrer and Comment spammers are a real pain in the a*se.&nbsp; Polluting my web logs and making any meaningful log analysis problematic.<br /><br />So, I now have an itch to scratch and I'm going to do something about it. I would encourage you, the reader, to do something about it too.<br /><br />Firstly, get yourself over to <a href="http://www.projecthoneypot.org/?rf=73568">Project Honey Pot</a> and read up on the project.&nbsp; If you can, set up a Honey Pot or two yourself. Also be sure to read about the <a href="http://www.projecthoneypot.org/faq.php#g">http:BL</a> - this works along similar lines to the DNS blacklists used for Email spammers.<br /><br />Next, I'm going to write a general Apache mod_perl module which will provide integration (lookup) to the http:BL and allow the user to "action"* the abusers.&nbsp; Minimally, it will prevent the normal apache log files from being polluted by diverting the log entries to a httpbl logfile.<br /><br />* "action" - To provide flexibility, I'm thinking of running an external script with the IP of the abuser.&nbsp; The script can then perform any action you wish. The one I'm going for is an iptables firewall block.<br /><br />Comments and suggestions welcome.<br /><br />Project Honey Pot has implementations for several languages, including <a href="http://www.php.net/">PHP</a> and <a href="http://www.perl.org/">Perl</a> (the languages that mean most to me).&nbsp; There may be an implementation for your <a href="http://www.projecthoneypot.org/httpbl_implementations.php">Web application </a> so you might not be interested in what I'm doing at all :)<br /><br /><span class="mt-enclosure mt-enclosure-image" style="display: inline;"><img alt="projhoneypot_banner.jpg" src="http://pgregg.com/blog/images/projhoneypot_banner.jpg" class="mt-image-none" style="" height="60" width="468" /></span><br /><br /><br /><div><br /></div>]]>
        
    </content>
</entry>

<entry>
    <title>Borland, Farewell my sweet.</title>
    <link rel="alternate" type="text/html" href="http://pgregg.com/blog/2009/05/borland-farewell-my-sweet.html" />
    <id>tag:pgregg.com,2009:/blog//2.334</id>

    <published>2009-05-06T14:31:36Z</published>
    <updated>2009-05-06T17:34:46Z</updated>

    <summary>Today, May 6 2009, marks the day that Borland, that once great master of all software development has finally recognised there was no other recourse but to up and sell itself off in order to survive.Back when I was a teenager, in the early 80s and personal computing was coming to the fore - I, and many others, aspired to work for that great company Borland. It was the pinnacle of language development and development tools and we wanted to work there. However, based in Ireland it was never to be.Also, once upon a time I happened to be working...</summary>
    <author>
        <name>Paul Gregg</name>
        <uri>http://pgregg.com</uri>
    </author>
    
        <category term="General" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="PHP" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="Software" scheme="http://www.sixapart.com/ns/types#category" />
    
    <category term="acquisitions" label="acquisitions" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="borland" label="Borland" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="delphi" label="delphi" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="jbuilder" label="jbuilder" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="ma" label="M&amp;A" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="software" label="software" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en" xml:base="http://pgregg.com/blog/">
        <![CDATA[Today, May 6 2009, marks the day that Borland, that once great master of all software development has finally recognised there was no other recourse but to up and sell itself off in order to survive.<br /><br />Back when I was a teenager, in the early 80s and personal computing was coming to the fore - I, and many others, aspired to work for that great company Borland. It was the pinnacle of language development and development tools and we wanted to work there. However, based in Ireland it was never to be.<br /><br />Also, once upon a time I happened to be working for a very promising young company with a fantastic product line called Segue Software, based in Boston, MA.&nbsp; Segue also had its troubles but a new CEO saw its fortunes turn and it was climbing to success. This success was noticed by the aforementioned Borland as it tried to re-invent itself as an Application Lifecycle Management (ALM) company.&nbsp; The same day it was announced that Borland was acquiring Segue, it also announced it was selling its developer tools division (that's Delphi, JBuilder, and later Delphi for PHP, 3rd Rail line of products).<br /><br />This was such a bitter-sweet time for many.&nbsp; I was overjoyed that I was going to work for Borland (childhood dream) - it didn't matter I wasn't going to work with the developer tools, working for "Borland" would just be cool.&nbsp; Sadness also because our little 200-man company was being consumed by a 1200-man behemoth (relatively) and no-matter which way you looked at it, people were going to lose their jobs.&nbsp; Pretty much the entire US East Coast staff (Segue Head Office) lost their jobs and the office was closed.&nbsp; Product development labs and Technical Support survived, simply by virtue that it was the product and product skills that were purchased, not the G&amp;A functions - they could go.<br /><br />I 'lived the dream' for the next 2.5 years in the IT department. Despite being remote, I loved working with the rest of the Borland teams as I was intimately involved in the merging/migration of Segue's systems into Borland's. I also had the pleasure of working with several departments to architect and deploy several new platforms (such as product downloads and licensing via Intraware, and the companies Salesforce.com, SFDC, deployment).&nbsp; I'll treasure the time I spent at Borland.<br /><br />Of course there were several WTF moments. Most significantly, for me, was the company "hanging its hat" on BMS (Business Management Solutions) which ultimately proved to be a hatstand made of jello. Very few, outside of management and that product team, believed in it.&nbsp; Another significant WTF for Borland was, If you plan to be the Application LIFECYCLE Management company - why divest yourself (for a paltry $27m) of two of the world's major AppDev toolsets (Delphi and JBuilder). You've just removed the feeder market and upsell opportunity into your ALM business. Finally, and internal WTF to get off my chest, on what planet does the IT department belong as a subdivision of the HR department?<br /><br />Borland will live on in the hearts of many of us who knew what she used to be. I think I left Borland a better place than I found it (as long as you don't look at the stock price ;), and I made some good friends. At the end of the day, there isn't much more you can ask from your tenure.<br />It is sad that today if you ask a typical Software Engineer if they know who Borland is, they'll respond "Who?" which typifies the company's slide into obscurity.<br /><br />I wish the best of luck to my former colleagues, who I'm sure, will be wondering what is to happen next. I also hope that the new owners, Micro Focus International (who?), have good fortune with their ALM drive. Perhaps the Borland name might live on as a brand for a suite of ALM products - who knows what they'll do.<br /><br />In the immortal words of Dr. Seuss "Don't cry because it's over. Smile because it happened."<br /><br /> ]]>
        
    </content>
</entry>

<entry>
    <title>PHP algorithms: Determining if an IP is within a specific range.</title>
    <link rel="alternate" type="text/html" href="http://pgregg.com/blog/2009/04/php-algorithms-determining-if-an-ip-is-within-a-specific-range.html" />
    <id>tag:pgregg.com,2009:/blog//2.333</id>

    <published>2009-04-27T22:48:15Z</published>
    <updated>2009-06-03T20:27:31Z</updated>

    <summary><![CDATA[I spend a lot of time lurking in the #PHP channel (efnet and freenode, please - no flamewars) and this topic is a commonly asked one that usually gets a simplified answer in the form of using strpos(), or at best an ip2long() in a greater than and less than answer. Unfortunately although people usually understand that an IP address is simply an unsigned 32 bit integer, and is easily determined, usually with $_SERVER['REMOTE_ADDR'], where the real challenge is - is in specifying the range within which they wish to check that IP address.&nbsp; IP ranges are usually specified in...]]></summary>
    <author>
        <name>Paul Gregg</name>
        <uri>http://pgregg.com</uri>
    </author>
    
        <category term="PHP" scheme="http://www.sixapart.com/ns/types#category" />
    
    <category term="algorithms" label="algorithms" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="cidr" label="CIDR" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="ipaddress" label="IP address" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="php" label="PHP" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en" xml:base="http://pgregg.com/blog/">
        <![CDATA[I spend a lot of time lurking in the #PHP channel (efnet and freenode, please - no flamewars) and this topic is a commonly asked one that usually gets a simplified answer in the form of using strpos(), or at best an ip2long() in a greater than and less than answer.
<br />
<br />Unfortunately although people usually understand that an IP address is simply an unsigned 32 bit integer, and is easily determined, usually with $_SERVER['REMOTE_ADDR'], where the real challenge is - is in specifying the range within which they wish to check that IP address.&nbsp; IP ranges are usually specified in three common ways (in increasing complexity):
<br />
<br />
<blockquote>
  <ol>
    <li>Wildcard: 192.168.10.*</li>
    <li>Start-End range: 10.1.0.0-10.1.255.255</li>
    <li>CIDR*: 172.16.1.0/24</li>
  </ol>
</blockquote>
<blockquote>* Classless Inter-Domain Routing
  <br />
</blockquote>
<p>The Wildcard method, or "classy", allows you to work at Class A (10.*.*.*), Class B (172.16.*.*) or Class C (192.168.10.*) levels of granularity which is how we used to do things in the old days (before the Web decided to make the Internet popular).&nbsp; But, increasingly, this just isn't granular enough for practical purposes.
  <br />
  <br />Thus was born CIDR (yes, I'm skipping talking about Start-End ranges for now).&nbsp; CIDR brought about the concept that we really didn't need to break networks on 8, 16, 24 bit boundaries and we could be more granular by allowing the use of any number (from 2-30) to specify a range of networks.&nbsp; Details on why you can't use "31" is beyond the scope of this article.
  <br />
  <br />CIDR renamed the former Class A, B and C networks as /8, /16 and /24 respectively and reflects the left-most significant bits of the 32-bit IP address.&nbsp; Thus was born the ability to specify very specific IP ranges in the form a.b.c.d/xx.&nbsp;&nbsp; However, part of the problem with this is that although it concisely describes the network start and end, most normal mortal humans couldn't decipher it. CIDR addressing can also be specified in the form of a longer netmask, e.g. a.b.c.d/255.255.255.224
  <br />
  <br />Thus, the simplified form of Start IP - End IP was put in place for mere mortals and is typically used by those without a networking background.&nbsp; It also features heavily in consumer broadband routers and notably in Microsoft Windows DHCP server.
  <br />
  <br />So having explained how a range, and by inference, that a netmask is, how can we use this knowledge to help us in determining if an IP is within a range?
  <br />
  <br />What this article will attempt to do is guide you though the construction of algorithms to make the checking of IPs simpler.
  <br />
  <br />Logically, Method 1 (the Wildcard), can be easily converted to Method 2 (Start-End range) by using setting Start and End to the Wildcard string and replacing the "*" character with 0 for the Start and 255 for the End, thus for example, "192.168.10.*" becomes "192.168.10.0-192.168.10.255" which should (I hope) be obvious to everyone.
  <br />
  <br />We can then proceed to evaluate both Method1 and Method2 in the same way.&nbsp; In this we're simply going to use the PHP built in function ip2long() on all 3 values and perform a mathematical check for Start &lt;= IP &lt;= End.
  <br />
  <br /><code><span style="color: rgb(0, 0, 0);"><span style="color: rgb(255, 128, 0);">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: rgb(0, 119, 0);">list(</span><span style="color: rgb(0, 0, 187);">$lower</span><span style="color: rgb(0, 119, 0);">,&nbsp;</span><span style="color: rgb(0, 0, 187);">$upper</span><span style="color: rgb(0, 119, 0);">)&nbsp;=&nbsp;</span><span style="color: rgb(0, 0, 187);">explode</span><span style="color: rgb(0, 119, 0);">(</span><span style="color: rgb(221, 0, 0);">'-'</span><span style="color: rgb(0, 119, 0);">,&nbsp;</span><span style="color: rgb(0, 0, 187);">$range</span><span style="color: rgb(0, 119, 0);">,&nbsp;</span><span style="color: rgb(0, 0, 187);">2</span><span style="color: rgb(0, 119, 0);">);
  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 187);">$lower_dec&nbsp;</span><span style="color: rgb(0, 119, 0);">=&nbsp;</span><span style="color: rgb(0, 0, 187);">ip2long</span><span style="color: rgb(0, 119, 0);">(</span><span style="color: rgb(0, 0, 187);">$lower</span><span style="color: rgb(0, 119, 0);">);
  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 187);">$upper_dec&nbsp;</span><span style="color: rgb(0, 119, 0);">=&nbsp;</span><span style="color: rgb(0, 0, 187);">ip2long</span><span style="color: rgb(0, 119, 0);">(</span><span style="color: rgb(0, 0, 187);">$upper</span><span style="color: rgb(0, 119, 0);">);
  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 187);">$ip_dec&nbsp;</span><span style="color: rgb(0, 119, 0);">=&nbsp;</span><span style="color: rgb(0, 0, 187);">ip2long</span><span style="color: rgb(0, 119, 0);">(</span><span style="color: rgb(0, 0, 187);">$ip</span><span style="color: rgb(0, 119, 0);">);
  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;(&nbsp;(</span><span style="color: rgb(0, 0, 187);">$ip_dec</span><span style="color: rgb(0, 119, 0);">&gt;=</span><span style="color: rgb(0, 0, 187);">$lower_dec</span><span style="color: rgb(0, 119, 0);">)&nbsp;&amp;&amp;&nbsp;(</span><span style="color: rgb(0, 0, 187);">$ip_dec</span><span style="color: rgb(0, 119, 0);">&lt;=</span><span style="color: rgb(0, 0, 187);">$upper_dec</span><span style="color: rgb(0, 119, 0);">)&nbsp;);</span></span></code>
  <br />
</p>
<p>We have, however, a complicating factor here - PHP does not do unsigned integers (32 bit) - which would be necessary for this math to work properly.&nbsp; We can negate this by switing to floating point data types.&nbsp; PHP stores&nbsp; floating types as 64 bit and so will have no problem with IPv4 address space (note - this obviously isn't granular enough for 128bit IPv6 addressing). Therefore the simplest way to solve the Start &lt;= IP &lt;= End problem with IPs and floating point numers is the following piece of code:
</p>
<p><code><span style="color: rgb(0, 0, 0);"><span style="color: rgb(0, 119, 0);">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: rgb(0, 0, 187);">$lower_dec&nbsp;</span><span style="color: rgb(0, 119, 0);">=&nbsp;(float)</span><span style="color: rgb(0, 0, 187);">sprintf</span><span style="color: rgb(0, 119, 0);">(</span><span style="color: rgb(221, 0, 0);">"%u"</span><span style="color: rgb(0, 119, 0);">,</span><span style="color: rgb(0, 0, 187);">ip2long</span><span style="color: rgb(0, 119, 0);">(</span><span style="color: rgb(0, 0, 187);">$lower</span><span style="color: rgb(0, 119, 0);">));
  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 187);">$upper_dec&nbsp;</span><span style="color: rgb(0, 119, 0);">=&nbsp;(float)</span><span style="color: rgb(0, 0, 187);">sprintf</span><span style="color: rgb(0, 119, 0);">(</span><span style="color: rgb(221, 0, 0);">"%u"</span><span style="color: rgb(0, 119, 0);">,</span><span style="color: rgb(0, 0, 187);">ip2long</span><span style="color: rgb(0, 119, 0);">(</span><span style="color: rgb(0, 0, 187);">$upper</span><span style="color: rgb(0, 119, 0);">));
  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 187);">$ip_dec&nbsp;</span><span style="color: rgb(0, 119, 0);">=&nbsp;(float)</span><span style="color: rgb(0, 0, 187);">sprintf</span><span style="color: rgb(0, 119, 0);">(</span><span style="color: rgb(221, 0, 0);">"%u"</span><span style="color: rgb(0, 119, 0);">,</span><span style="color: rgb(0, 0, 187);">ip2long</span><span style="color: rgb(0, 119, 0);">(</span><span style="color: rgb(0, 0, 187);">$ip</span><span style="color: rgb(0, 119, 0);">));
  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;(&nbsp;(</span><span style="color: rgb(0, 0, 187);">$ip_dec</span><span style="color: rgb(0, 119, 0);">&gt;=</span><span style="color: rgb(0, 0, 187);">$lower_dec</span><span style="color: rgb(0, 119, 0);">)&nbsp;&amp;&amp;&nbsp;(</span><span style="color: rgb(0, 0, 187);">$ip_dec</span><span style="color: rgb(0, 119, 0);">&lt;=</span><span style="color: rgb(0, 0, 187);">$upper_dec</span><span style="color: rgb(0, 119, 0);">)&nbsp;);
  <br /></span></span></code>
</p>
<p>Next we have the challenge of handing the CIDR netmasks. What we could do is to take a CIDR format IPaddress/netmask and calculate the Start and End IPs of that block and proceed as before - but that would be no fun - and would mean I haven't really taught anything through this article.&nbsp;
  <br />
  <br />The method we're going to use here is how all the world's Internet routers determine if a destination IP is in a specific CIDR address space. And we're going to get down and dirty with bitmasks and logical bitwise operators.
  <br />
  <br />So using a real world example, my webserver IP 80.76.201.37 and the netblock within which it resides is 80.76.201.32/27, how does this all work?
  <br />
  <br />Well the /27 indicates that the first 27 bits of the IP address are the same network and IP address in that network (range) will have those same identical first 27 bits.&nbsp; Bits 28-32 are variable and allow 5 bits of variation.&nbsp; If you know your binary, then this means 32 possible IPs. (However with routing, you can't use the bottom and top IP from any range as these are special and mean the network and broadcast addresses respectively. [This is also why a /31 isn't much use (except for PPP links) as you can't use the 2 addresses that space gives you]).
  <br />
  <br />So thinking logically, bitwise, if I take my IP address and the CIDR spec, then all I have to do is check that the first 27 bits all match and I'm good. Correct.&nbsp; So how would we do this in PHP? Sound's simple, lets just use PHP's bitwise logical AND operator: &amp;
  <br />Again, correct.&nbsp;
  <br />
  <br />In order to do this we need to convert 27 into what 27 really means - a 32 bit number of 27 ones and 5 zeros in binary (which is what 255.255.255.224 really looks like).
  <br />
  <br />In pseudo-code you could then do if (IP &amp; BITMASK) == (RANGE &amp; BITMASK) then all is good and you know that the IP is within the range.
  <br />
  <br />Visualising this using our real IP address (using the very handy unix tool ipcalc):
  <br />
</p>
<blockquote>
  <pre><font face="courier new,courier,monospace"><small>Address:   <strong>80.76.201.37</strong>    <strong>01010000.01001100.11001001.001</strong>00101
Netmask:   255.255.255.224 11111111.11111111.11111111.11100000
Wildcard:  0.0.0.31        00000000.00000000.00000000.000<strong>11111</strong>

Network:   <strong>80.76.201.32/27</strong> <strong>01010000.01001100.11001001.001</strong>00000
HostMin:   80.76.201.33    01010000.01001100.11001001.00100001
HostMax:   80.76.201.62    01010000.01001100.11001001.00111110
Broadcast: <strong>80.76.201.63</strong>    <strong>01010000.01001100.11001001.00111111</strong>
Hosts/Net: 30</small></font></pre>
</blockquote>You can see this in the Wildcard line of 0.0.0.31, and the Network ORed with Wildcard results in the Broadcast address: 80.76.201.63.
<br />
<br />Knowing this, then the IP address ANDed with the Network address will result in the same value as the Range ANDed with the Network address and so can be used as a comparison for an IP residing within that broadcast range.
<br />
<br />How can we work out this Network address in PHP, again we have two strategies, one is to so a simple substr() and take the left most significant bits of the range and then simply pad out to the right with 0s.&nbsp; Or we can do some math with "NOT of 2 to the power of (32-range) - 1". Thus for our value /27 this gives us the decimal value 31, NOTed results in (65536-31)&nbsp; (representational in the bit form - PHP will see it as a negative integer, but we don't need to worry about that).
<br />
<br />I'm sure by now, your screaming for some code (and if you stuck around this long, you really deserve it).
<br />
<br />Code to manipulate a range/netmask into a broadcast address, using math, assuming:
<br />
<blockquote><code><span style="color: rgb(0, 0, 0);"><span style="color: rgb(0, 0, 187);">$ip&nbsp;</span><span style="color: rgb(0, 119, 0);">=&nbsp;</span><span style="color: rgb(221, 0, 0);">"80.76.201.37"</span><span style="color: rgb(0, 119, 0);">;</span></span></code>
  <br /><code><span style="color: rgb(0, 0, 0);"><span style="color: rgb(0, 119, 0);"></span><span style="color: rgb(0, 0, 187);">$range&nbsp;</span><span style="color: rgb(0, 119, 0);">=&nbsp;</span><span style="color: rgb(221, 0, 0);">"80.76.201.32"</span><span style="color: rgb(0, 119, 0);">;</span></span></code>
  <br /><code><span style="color: rgb(0, 0, 0);"><span style="color: rgb(0, 119, 0);"></span><span style="color: rgb(0, 0, 187);">$netmask&nbsp;</span><span style="color: rgb(0, 119, 0);">=&nbsp;</span><span style="color: rgb(0, 0, 187);">27</span><span style="color: rgb(0, 119, 0);">;</span></span></code>
  <br /><code><span style="color: rgb(0, 0, 0);"><span style="color: rgb(0, 119, 0);"></span></span></code>
</blockquote> We can convert the IPs to long integers using ip2long (denoted by variable_dec - dec being short for decimal):
<br />
<blockquote><code><span style="color: rgb(0, 0, 0);"><span style="color: rgb(0, 119, 0);"></span><span style="color: rgb(0, 0, 187);">$range_dec&nbsp;</span><span style="color: rgb(0, 119, 0);">=&nbsp;</span><span style="color: rgb(0, 0, 187);">ip2long</span><span style="color: rgb(0, 119, 0);">(</span><span style="color: rgb(0, 0, 187);">$range</span><span style="color: rgb(0, 119, 0);">);</span></span></code>
  <br /><code><span style="color: rgb(0, 0, 0);"><span style="color: rgb(0, 119, 0);"></span><span style="color: rgb(0, 0, 187);">$ip_dec&nbsp;</span><span style="color: rgb(0, 119, 0);">=&nbsp;</span><span style="color: rgb(0, 0, 187);">ip2long</span><span style="color: rgb(0, 119, 0);">(</span><span style="color: rgb(0, 0, 187);">$ip</span><span style="color: rgb(0, 119, 0);">);</span></span></code>
  <br />
</blockquote>This gives us the basis of our math, we now just need to work out the broadcast address.
<br />
<br />Strategy 1 using str_pad to create a string by padding with 1s and 0s.
<br />
<blockquote><code><span style="color: rgb(0, 0, 0);"><span style="color: rgb(0, 119, 0);"></span></span></code><code><span style="color: rgb(0, 0, 0);"></span></code><code><span style="color: rgb(0, 0, 0);"><span style="color: rgb(255, 128, 0);"></span><span style="color: rgb(0, 0, 187);">$netmask_dec&nbsp;</span><span style="color: rgb(0, 119, 0);">= </span></span></code><code><span style="color: rgb(0, 0, 0);"><span style="color: rgb(0, 119, 0);"></span><span style="color: rgb(0, 0, 187);">bindec( str_pad</span><span style="color: rgb(0, 119, 0);">(</span><span style="color: rgb(221, 0, 0);">''</span><span style="color: rgb(0, 119, 0);">, </span><span style="color: rgb(0, 0, 187);"></span><span style="color: rgb(0, 119, 0);"></span><span style="color: rgb(0, 0, 187);">$netmask</span><span style="color: rgb(0, 119, 0);">,&nbsp;</span><span style="color: rgb(221, 0, 0);">'1'</span><span style="color: rgb(0, 119, 0);">)</span></span></code>
  <br /><code><span style="color: rgb(0, 0, 0);"><span style="color: rgb(0, 119, 0);">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; . </span><span style="color: rgb(0, 0, 187);">str_pad</span><span style="color: rgb(0, 119, 0);">(</span><span style="color: rgb(221, 0, 0);">''</span><span style="color: rgb(0, 119, 0);">,&nbsp;</span><span style="color: rgb(0, 0, 187);">32</span><span style="color: rgb(0, 119, 0);">-</span><span style="color: rgb(0, 0, 187);">$netmask</span><span style="color: rgb(0, 119, 0);">,&nbsp;</span><span style="color: rgb(221, 0, 0);">'0'</span><span style="color: rgb(0, 119, 0);">) );</span></span></code>
  <br /><code><span style="color: rgb(0, 0, 0);"><span style="color: rgb(0, 119, 0);"></span></span></code>
</blockquote>We can achieve the same result though mathematics by NOTtin the wildcard value. This is our Strategy 2:
<br />
<blockquote><code><span style="color: rgb(0, 0, 0);"><span style="color: rgb(255, 128, 0);"></span><span style="color: rgb(0, 0, 187);">$wildcard_dec&nbsp;</span><span style="color: rgb(0, 119, 0);">=&nbsp;</span><span style="color: rgb(0, 0, 187);">pow</span><span style="color: rgb(0, 119, 0);">(</span><span style="color: rgb(0, 0, 187);">2</span><span style="color: rgb(0, 119, 0);">,&nbsp;(</span><span style="color: rgb(0, 0, 187);">32</span><span style="color: rgb(0, 119, 0);">-</span><span style="color: rgb(0, 0, 187);">$netmask</span><span style="color: rgb(0, 119, 0);">))&nbsp;-&nbsp;</span><span style="color: rgb(0, 0, 187);">1</span><span style="color: rgb(0, 119, 0);">;</span></span></code>
  <br /><code><span style="color: rgb(0, 0, 0);"><span style="color: rgb(0, 0, 187);">$netmask_dec&nbsp;</span><span style="color: rgb(0, 119, 0);">=&nbsp;~&nbsp;</span><span style="color: rgb(0, 0, 187);">$wildcard_dec</span><span style="color: rgb(0, 119, 0);">;</span></span></code>
  <br />
</blockquote>Once we know the netmask address (in decimal) as we have here, we can know that, if by ANDing this with the original IP to check results against the Range ANDed with the Netmask, then the IP is within the range defined by the range/mask.
<br />
<br />This can be checked easily with:
<br />
<blockquote><code><span style="color: rgb(0, 0, 0);"><span style="color: rgb(0, 119, 0);">return&nbsp;((</span><span style="color: rgb(0, 0, 187);">$ip_dec&nbsp;</span><span style="color: rgb(0, 119, 0);">&amp;&nbsp;</span><span style="color: rgb(0, 0, 187);">$netmask_dec</span><span style="color: rgb(0, 119, 0);">)&nbsp;==&nbsp;(</span><span style="color: rgb(0, 0, 187);">$range_dec&nbsp;</span><span style="color: rgb(0, 119, 0);">&amp;&nbsp;</span><span style="color: rgb(0, 0, 187);">$netmask_dec</span><span style="color: rgb(0, 119, 0);">));</span></span></code>
  <br /><code><span style="color: rgb(0, 0, 0);"><span style="color: rgb(0, 119, 0);"></span></span></code>
</blockquote><code><span style="color: rgb(0, 0, 0);"><span style="color: rgb(0, 119, 0);"></span></span></code>I have pulled all of this logic together in a easily included file to provide a single function called ip_in_range($ip, $range) in which $ip is the IP address you want to validate and $range is a any of the above formats, Wildcard, Start-End addressing or CIDR.&nbsp; The function will return a simple TRUE or FALSE if the IP is in that range.
<br />
<br />The source code to the all-in function is available here:
<br />
<blockquote><a href="http://pgregg.com/projects/php/ip_in_range/ip_in_range.phps">http://pgregg.com/projects/php/ip_in_range/ip_in_range.phps</a>
  <br />
</blockquote>With an example run (and source code):
<br />
<blockquote><a href="http://pgregg.com/projects/php/ip_in_range/test.php">http://pgregg.com/projects/php/ip_in_range/test.php</a>
  <br />
</blockquote>
<p>I hope this article has been educational, please feel free to leave comments or feedback.
  <br />
</p>
<p>Update: There have been questions about PHP's signed integers and my use of bit operations in the code.&nbsp;&nbsp; It is important to recognise that when dealing with signed or unsigned 32 bit integers purely as bit patterns for masking with a netmask or broadcast address pattern - the fact that a number (128.0.0.0 or above) really is negative, doesn't have any impact on the validity of the result.&nbsp; The only impact to not having signed 32 bit integers is in the Start-End range check (example 2 above: 10.1.0.0-10.1.255.255) where a range spanning the switch from positive to negative would be catastrophic to the check.&nbsp; We can safely work around that problem by using floating point numbers as we are only doing &lt;= and &gt;= comparisons and not attempting any bitwise operators (which don't work on floats).
</p>]]>
        
    </content>
</entry>

<entry>
    <title>tail -# file, in PHP</title>
    <link rel="alternate" type="text/html" href="http://pgregg.com/blog/2009/04/tail---file-in-php.html" />
    <id>tag:pgregg.com,2009:/blog//2.331</id>

    <published>2009-04-23T20:05:48Z</published>
    <updated>2009-06-03T20:18:17Z</updated>

    <summary><![CDATA[I read Kevin's Read Line from File article today and thought I would add some sample code I wrote a while back to address a similar task in PHP - How to perform the equivalent of "tail -# filename" in PHP.A typical task in some applications such as a shoutbox or a simple log file viewer is how to extract the last 'n' lines from a (very) large file - efficiently.&nbsp; There are several approaches one can take to solving the problem (as always in programming there are many ways to skin the proverbial cat) - taking the easy way...]]></summary>
    <author>
        <name>Paul Gregg</name>
        <uri>http://pgregg.com</uri>
    </author>
    
        <category term="PHP" scheme="http://www.sixapart.com/ns/types#category" />
    
    <category term="algorithms" label="algorithms" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="file" label="file" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="php" label="PHP" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="tail" label="tail" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en" xml:base="http://pgregg.com/blog/">
        <![CDATA[I read Kevin's <a href="http://phpro.org/examples/Read-Line-From-File.html">Read Line from File</a> article today and thought I would add some sample code I wrote a while back to address a similar task in PHP - How to perform the equivalent of "tail -# filename" in PHP.<br /><br />A typical task in some applications such as a shoutbox or a simple log file viewer is how to extract the last 'n' lines from a (very) large file - efficiently.&nbsp; There are several approaches one can take to solving the problem (as always in programming there are many ways to skin the proverbial cat) - taking the easy way out and shelling out to run the tail command; or reading the file line by line keeping the last 'n' lines in memory with a queue.<br /><br />However, here I will demonstrate a fairly tuned method of seeking to the end of the file and stepping back to read sufficient lines to the end of the file. If insufficient lines are returned, it incrementally looks back further in the file until it either can look no further, or sufficient lines are returned. <br /><br />Assumptions need to be made in order to tune the algorithm for the competing challenges of :<br /><ul><li>Reading enough lines in</li><li>as few reads as possible</li></ul>My approach to this is to provide an approximation to the average size of a line:<code> <span style="color: rgb(0, 0, 0);"><span style="color: rgb(0, 0, 187);">$linelength</span></span></code><br />Multiply by 'n': <code><span style="color: rgb(0, 0, 0);"><span style="color: rgb(0, 0, 187);">$linecount</span></span></code><br />and we have the byte <code><span style="color: rgb(0, 0, 0);"><span style="color: rgb(0, 0, 187);">$offset </span></span></code>from the end of the file that we will seek to to begin reading.<br /><br />A check we need to do right here is that we haven't offset to before the beginning of the file, and so we have to override <code><span style="color: rgb(0, 0, 0);"><span style="color: rgb(0, 0, 187);">$offset </span></span></code>to match the file size.<br /><br />Also as I'm being over-cautious, I'm going to tell it to offset <code><span style="color: rgb(0, 0, 0);"><span style="color: rgb(0, 0, 187);">$linecount + 1</span></span></code> - The main reason for this is by seeking to a specific byte location in the file, we would have to be very lucky to land on the first character of a new line - therefore we must perform a fgets() and throw away that result.<br /><br />Typically, I want it to be able to read 'n' lines forward from the offset given, however if that proves insufficient, I'm going to grow the offset by 10% and try again. I also want to make it so that the algorithm is better able to tune itself if we grossly underestimate what <code><span style="color: rgb(0, 0, 0);"><span style="color: rgb(0, 0, 187);">$linelength </span></span></code>should be.&nbsp; In order to do this, we're going to track the string length of each line we do get back and adjust the offset accordingly.<br /><br />In our example, lets try reading the last 10 lines from Apache's access_log<br /><br />So let's look at the code so far, nothing interesting, we're just prepping for the interesting stuff:<br /><blockquote><code><span style="color: rgb(0, 0, 0);"><span style="color: rgb(0, 0, 187);">$linecount&nbsp;&nbsp;</span><span style="color: rgb(0, 119, 0);">=&nbsp;</span><span style="color: rgb(0, 0, 187);">10</span><span style="color: rgb(0, 119, 0);">;&nbsp;&nbsp;</span><span style="color: rgb(255, 128, 0);">//&nbsp;Number&nbsp;of&nbsp;lines&nbsp;we&nbsp;want&nbsp;to&nbsp;read<br /></span><span style="color: rgb(0, 0, 187);">$linelength&nbsp;</span><span style="color: rgb(0, 119, 0);">=&nbsp;</span><span style="color: rgb(0, 0, 187);">160</span><span style="color: rgb(0, 119, 0);">;&nbsp;</span><span style="color: rgb(255, 128, 0);">//&nbsp;Apache's&nbsp;logs&nbsp;are&nbsp;typically&nbsp;~200+&nbsp;chars<br />//&nbsp;I've&nbsp;set&nbsp;this&nbsp;to&nbsp;&lt;&nbsp;200&nbsp;to&nbsp;show&nbsp;the&nbsp;dynamic&nbsp;nature&nbsp;of&nbsp;the&nbsp;algorithm<br />//&nbsp;offset&nbsp;correction.<br /></span><span style="color: rgb(0, 0, 187);">$file&nbsp;</span><span style="color: rgb(0, 119, 0);">=&nbsp;</span><span style="color: rgb(221, 0, 0);">'/usr/local/apache2/logs/access_log.demo'</span><span style="color: rgb(0, 119, 0);">;<br /></span><span style="color: rgb(0, 0, 187);">$fsize&nbsp;</span><span style="color: rgb(0, 119, 0);">=&nbsp;</span><span style="color: rgb(0, 0, 187);">filesize</span><span style="color: rgb(0, 119, 0);">(</span><span style="color: rgb(0, 0, 187);">$file</span><span style="color: rgb(0, 119, 0);">);</span></span></code><br /><code><span style="color: rgb(0, 0, 0);"><span style="color: rgb(0, 119, 0);"></span></span></code><br /><code><span style="color: rgb(0, 0, 0);"><span style="color: rgb(0, 119, 0);"></span><span style="color: rgb(255, 128, 0);">//&nbsp;check&nbsp;if&nbsp;file&nbsp;is&nbsp;smaller&nbsp;than&nbsp;possible&nbsp;max&nbsp;lines</span></span></code><br /><code><span style="color: rgb(0, 0, 0);"><span style="color: rgb(255, 128, 0);"></span><span style="color: rgb(0, 0, 187);">$offset&nbsp;</span><span style="color: rgb(0, 119, 0);">=&nbsp;(</span><span style="color: rgb(0, 0, 187);">$linecount</span><span style="color: rgb(0, 119, 0);">+</span><span style="color: rgb(0, 0, 187);">1</span><span style="color: rgb(0, 119, 0);">)&nbsp;*&nbsp;</span><span style="color: rgb(0, 0, 187);">$linelength</span><span style="color: rgb(0, 119, 0);">;</span></span></code><br /><code><span style="color: rgb(0, 0, 0);"><span style="color: rgb(0, 119, 0);">if&nbsp;(</span><span style="color: rgb(0, 0, 187);">$offset&nbsp;</span><span style="color: rgb(0, 119, 0);">&gt;&nbsp;</span><span style="color: rgb(0, 0, 187);">$fsize</span><span style="color: rgb(0, 119, 0);">)&nbsp;</span><span style="color: rgb(0, 0, 187);">$offset&nbsp;</span><span style="color: rgb(0, 119, 0);">=&nbsp;</span><span style="color: rgb(0, 0, 187);">$fsize</span><span style="color: rgb(0, 119, 0);">;</span></span></code><br /><code><span style="color: rgb(0, 0, 0);"><span style="color: rgb(0, 119, 0);"></span></span></code></blockquote><code><span style="color: rgb(0, 0, 0);"><span style="color: rgb(0, 119, 0);"></span></span></code>Next up we're going to open the file and using our method of seeking to the end of the file, less our offset, here is the meat of our routine:<br /><br /><blockquote><code><span style="color: rgb(0, 0, 0);"><span style="color: rgb(0, 0, 187);"></span></span></code><code><span style="color: rgb(0, 0, 0);"><span style="color: rgb(0, 0, 187);">$fp&nbsp;</span><span style="color: rgb(0, 119, 0);">=&nbsp;</span><span style="color: rgb(0, 0, 187);">fopen</span><span style="color: rgb(0, 119, 0);">(</span><span style="color: rgb(0, 0, 187);">$file</span><span style="color: rgb(0, 119, 0);">,&nbsp;</span><span style="color: rgb(221, 0, 0);">'r'</span><span style="color: rgb(0, 119, 0);">);<br />if&nbsp;(</span><span style="color: rgb(0, 0, 187);">$fp&nbsp;</span><span style="color: rgb(0, 119, 0);">===&nbsp;</span><span style="color: rgb(0, 0, 187);">false</span><span style="color: rgb(0, 119, 0);">)&nbsp;exit;<br /><br /></span><span style="color: rgb(0, 0, 187);">$lines&nbsp;</span><span style="color: rgb(0, 119, 0);">=&nbsp;array();&nbsp;</span><span style="color: rgb(255, 128, 0);">//&nbsp;array&nbsp;to&nbsp;store&nbsp;the&nbsp;lines&nbsp;we&nbsp;read.<br /><br /></span><span style="color: rgb(0, 0, 187);">$readloop&nbsp;</span><span style="color: rgb(0, 119, 0);">=&nbsp;</span><span style="color: rgb(0, 0, 187);">true</span><span style="color: rgb(0, 119, 0);">;<br />while(</span><span style="color: rgb(0, 0, 187);">$readloop</span><span style="color: rgb(0, 119, 0);">)&nbsp;{<br />&nbsp;&nbsp;</span><span style="color: rgb(255, 128, 0);">//&nbsp;we&nbsp;will&nbsp;finish&nbsp;reading&nbsp;when&nbsp;we&nbsp;have&nbsp;read&nbsp;$linecount&nbsp;lines,&nbsp;or&nbsp;the&nbsp;file<br />&nbsp;&nbsp;//&nbsp;just&nbsp;doesn't&nbsp;have&nbsp;$linecount&nbsp;lines<br /><br />&nbsp;&nbsp;//&nbsp;seek&nbsp;to&nbsp;$offset&nbsp;bytes&nbsp;from&nbsp;the&nbsp;end&nbsp;of&nbsp;the&nbsp;file<br />&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 187);">fseek</span><span style="color: rgb(0, 119, 0);">(</span><span style="color: rgb(0, 0, 187);">$fp</span><span style="color: rgb(0, 119, 0);">,&nbsp;</span><span style="color: rgb(0, 0, 187);">0&nbsp;</span><span style="color: rgb(0, 119, 0);">-&nbsp;</span><span style="color: rgb(0, 0, 187);">$offset</span><span style="color: rgb(0, 119, 0);">,&nbsp;</span><span style="color: rgb(0, 0, 187);">SEEK_END</span><span style="color: rgb(0, 119, 0);">);<br /><br /></span></span></code><code><span style="color: rgb(0, 0, 0);"><span style="color: rgb(0, 119, 0);">&nbsp; </span><span style="color: rgb(255, 128, 0);">//&nbsp;discard&nbsp;the&nbsp;first&nbsp;line&nbsp;as&nbsp;it&nbsp;won't&nbsp;be&nbsp;a&nbsp;complete&nbsp;line<br />&nbsp;&nbsp;//&nbsp;unless&nbsp;we're&nbsp;right&nbsp;at&nbsp;the&nbsp;start&nbsp;of&nbsp;the&nbsp;file<br />&nbsp;&nbsp;</span><span style="color: rgb(0, 119, 0);">if&nbsp;(</span><span style="color: rgb(0, 0, 187);">$offset&nbsp;</span><span style="color: rgb(0, 119, 0);">!=&nbsp;</span><span style="color: rgb(0, 0, 187);">$fsize</span><span style="color: rgb(0, 119, 0);">)&nbsp;</span><span style="color: rgb(0, 0, 187);">fgets</span><span style="color: rgb(0, 119, 0);">(</span><span style="color: rgb(0, 0, 187);">$fp</span><span style="color: rgb(0, 119, 0);">);</span></span></code><code><span style="color: rgb(0, 0, 0);"><span style="color: rgb(0, 119, 0);"></span><span style="color: rgb(0, 119, 0);"><br /><br />&nbsp;&nbsp;</span><span style="color: rgb(255, 128, 0);">//&nbsp;tally&nbsp;of&nbsp;the&nbsp;number&nbsp;of&nbsp;bytes&nbsp;in&nbsp;each&nbsp;line&nbsp;we&nbsp;read<br />&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 187);">$linesize&nbsp;</span><span style="color: rgb(0, 119, 0);">=&nbsp;</span><span style="color: rgb(0, 0, 187);">0</span><span style="color: rgb(0, 119, 0);">;<br /><br />&nbsp;&nbsp;</span><span style="color: rgb(255, 128, 0);">//&nbsp;read&nbsp;from&nbsp;here&nbsp;till&nbsp;the&nbsp;end&nbsp;of&nbsp;the&nbsp;file&nbsp;and&nbsp;remember&nbsp;each&nbsp;line<br />&nbsp;&nbsp;</span><span style="color: rgb(0, 119, 0);">while(</span><span style="color: rgb(0, 0, 187);">$line&nbsp;</span><span style="color: rgb(0, 119, 0);">=&nbsp;</span><span style="color: rgb(0, 0, 187);">fgets</span><span style="color: rgb(0, 119, 0);">(</span><span style="color: rgb(0, 0, 187);">$fp</span><span style="color: rgb(0, 119, 0);">))&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 187);">array_push</span><span style="color: rgb(0, 119, 0);">(</span><span style="color: rgb(0, 0, 187);">$lines</span><span style="color: rgb(0, 119, 0);">,&nbsp;</span><span style="color: rgb(0, 0, 187);">$line</span><span style="color: rgb(0, 119, 0);">);<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 187);">$linesize&nbsp;</span><span style="color: rgb(0, 119, 0);">+=&nbsp;</span><span style="color: rgb(0, 0, 187);">strlen</span><span style="color: rgb(0, 119, 0);">(</span><span style="color: rgb(0, 0, 187);">$line</span><span style="color: rgb(0, 119, 0);">);&nbsp;</span><span style="color: rgb(255, 128, 0);">//&nbsp;total&nbsp;up&nbsp;the&nbsp;char&nbsp;count<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;if&nbsp;we've&nbsp;been&nbsp;able&nbsp;to&nbsp;get&nbsp;more&nbsp;lines&nbsp;than&nbsp;we&nbsp;need<br />&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;lose&nbsp;the&nbsp;first&nbsp;entry&nbsp;in&nbsp;the&nbsp;queue<br />&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Logically&nbsp;we&nbsp;should&nbsp;decrement&nbsp;$linesize&nbsp;too,&nbsp;but&nbsp;if&nbsp;we<br />&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;hit&nbsp;the&nbsp;magic&nbsp;number&nbsp;of&nbsp;lines,&nbsp;we&nbsp;are&nbsp;never&nbsp;going&nbsp;to&nbsp;use&nbsp;it<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 119, 0);">if&nbsp;(</span><span style="color: rgb(0, 0, 187);">count</span><span style="color: rgb(0, 119, 0);">(</span><span style="color: rgb(0, 0, 187);">$lines</span><span style="color: rgb(0, 119, 0);">)&nbsp;&gt;&nbsp;</span><span style="color: rgb(0, 0, 187);">$linecount</span><span style="color: rgb(0, 119, 0);">)&nbsp;</span><span style="color: rgb(0, 0, 187);">array_shift</span><span style="color: rgb(0, 119, 0);">(</span><span style="color: rgb(0, 0, 187);">$lines</span><span style="color: rgb(0, 119, 0);">);<br />&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;</span><span style="color: rgb(255, 128, 0);">//&nbsp;We&nbsp;have&nbsp;now&nbsp;read&nbsp;all&nbsp;the&nbsp;lines&nbsp;from&nbsp;$offset&nbsp;until&nbsp;the&nbsp;end&nbsp;of&nbsp;the&nbsp;file<br />&nbsp;&nbsp;</span><span style="color: rgb(0, 119, 0);">if&nbsp;(</span><span style="color: rgb(0, 0, 187);">count</span><span style="color: rgb(0, 119, 0);">(</span><span style="color: rgb(0, 0, 187);">$lines</span><span style="color: rgb(0, 119, 0);">)&nbsp;==&nbsp;</span><span style="color: rgb(0, 0, 187);">$linecount</span><span style="color: rgb(0, 119, 0);">)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(255, 128, 0);">//&nbsp;perfect&nbsp;-&nbsp;have&nbsp;enough&nbsp;lines,&nbsp;can&nbsp;exit&nbsp;the&nbsp;loop<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 187);">$readloop&nbsp;</span><span style="color: rgb(0, 119, 0);">=&nbsp;</span><span style="color: rgb(0, 0, 187);">false</span><span style="color: rgb(0, 119, 0);">;<br />&nbsp;&nbsp;}&nbsp;elseif&nbsp;(</span><span style="color: rgb(0, 0, 187);">$offset&nbsp;</span><span style="color: rgb(0, 119, 0);">&gt;=&nbsp;</span><span style="color: rgb(0, 0, 187);">$fsize</span><span style="color: rgb(0, 119, 0);">)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(255, 128, 0);">//&nbsp;file&nbsp;is&nbsp;too&nbsp;small&nbsp;-&nbsp;nothing&nbsp;more&nbsp;we&nbsp;can&nbsp;do,&nbsp;we&nbsp;must&nbsp;exit&nbsp;the&nbsp;loop<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 187);">$readloop&nbsp;</span><span style="color: rgb(0, 119, 0);">=&nbsp;</span><span style="color: rgb(0, 0, 187);">false</span><span style="color: rgb(0, 119, 0);">;<br />&nbsp;&nbsp;}&nbsp;elseif&nbsp;(</span><span style="color: rgb(0, 0, 187);">count</span><span style="color: rgb(0, 119, 0);">(</span><span style="color: rgb(0, 0, 187);">$lines</span><span style="color: rgb(0, 119, 0);">)&nbsp;&lt;&nbsp;</span><span style="color: rgb(0, 0, 187);">$linecount</span><span style="color: rgb(0, 119, 0);">)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(255, 128, 0);">//&nbsp;try&nbsp;again&nbsp;with&nbsp;a&nbsp;bigger&nbsp;offset<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 187);">$offset&nbsp;</span><span style="color: rgb(0, 119, 0);">=&nbsp;</span><span style="color: rgb(0, 0, 187);">intval</span><span style="color: rgb(0, 119, 0);">(</span><span style="color: rgb(0, 0, 187);">$offset&nbsp;</span><span style="color: rgb(0, 119, 0);">*&nbsp;</span><span style="color: rgb(0, 0, 187);">1.1</span><span style="color: rgb(0, 119, 0);">);&nbsp;&nbsp;</span><span style="color: rgb(255, 128, 0);">//&nbsp;increase&nbsp;offset&nbsp;10%<br />&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;but&nbsp;also&nbsp;work&nbsp;out&nbsp;what&nbsp;the&nbsp;offset&nbsp;could&nbsp;be&nbsp;if&nbsp;based&nbsp;on&nbsp;the&nbsp;lines&nbsp;we&nbsp;saw<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 187);">$offset2&nbsp;</span><span style="color: rgb(0, 119, 0);">=&nbsp;</span><span style="color: rgb(0, 0, 187);">intval</span><span style="color: rgb(0, 119, 0);">(</span><span style="color: rgb(0, 0, 187);">$linesize</span><span style="color: rgb(0, 119, 0);">/</span><span style="color: rgb(0, 0, 187);">count</span><span style="color: rgb(0, 119, 0);">(</span><span style="color: rgb(0, 0, 187);">$lines</span><span style="color: rgb(0, 119, 0);">)&nbsp;* </span>(<span style="color: rgb(0, 0, 187);">$linecount+1)</span><span style="color: rgb(0, 119, 0);">);<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(255, 128, 0);">//&nbsp;and&nbsp;if&nbsp;it&nbsp;is&nbsp;larger,&nbsp;then&nbsp;use&nbsp;that&nbsp;one&nbsp;instead&nbsp;(self-tuning)<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 119, 0);">if&nbsp;(</span><span style="color: rgb(0, 0, 187);">$offset2&nbsp;</span><span style="color: rgb(0, 119, 0);">&gt;&nbsp;</span><span style="color: rgb(0, 0, 187);">$offset</span><span style="color: rgb(0, 119, 0);">)&nbsp;</span><span style="color: rgb(0, 0, 187);">$offset&nbsp;</span><span style="color: rgb(0, 119, 0);">=&nbsp;</span><span style="color: rgb(0, 0, 187);">$offset2</span><span style="color: rgb(0, 119, 0);">;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(255, 128, 0);">//&nbsp;Also&nbsp;remember&nbsp;we&nbsp;can't&nbsp;seek&nbsp;back&nbsp;past&nbsp;the&nbsp;start&nbsp;of&nbsp;the&nbsp;file<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 119, 0);">if&nbsp;(</span><span style="color: rgb(0, 0, 187);">$offset&nbsp;</span><span style="color: rgb(0, 119, 0);">&gt;&nbsp;</span><span style="color: rgb(0, 0, 187);">$fsize</span><span style="color: rgb(0, 119, 0);">)&nbsp;</span><span style="color: rgb(0, 0, 187);">$offset&nbsp;</span><span style="color: rgb(0, 119, 0);">=&nbsp;</span><span style="color: rgb(0, 0, 187);">$fsize</span><span style="color: rgb(0, 119, 0);">;<br />&nbsp;&nbsp;&nbsp;&nbsp;echo&nbsp;</span><span style="color: rgb(221, 0, 0);">'Trying&nbsp;with&nbsp;a&nbsp;bigger&nbsp;offset:&nbsp;'</span><span style="color: rgb(0, 119, 0);">,&nbsp;</span><span style="color: rgb(0, 0, 187);">$offset</span><span style="color: rgb(0, 119, 0);">,&nbsp;</span><span style="color: rgb(221, 0, 0);">"\n"</span><span style="color: rgb(0, 119, 0);">;<br /></span></span></code><code><span style="color: rgb(0, 0, 0);"><span style="color: rgb(0, 119, 0);">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(255, 128, 0);">//&nbsp;and&nbsp;reset<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 187);">$lines&nbsp;</span><span style="color: rgb(0, 119, 0);">=&nbsp;array();<br /></span></span></code><code><span style="color: rgb(0, 0, 0);"><span style="color: rgb(0, 119, 0);">&nbsp; }<br />}<br /><br /></span><span style="color: rgb(255, 128, 0);">//&nbsp;Let's&nbsp;have&nbsp;a&nbsp;look&nbsp;at&nbsp;the&nbsp;lines&nbsp;we&nbsp;read.<br /></span><span style="color: rgb(0, 0, 187);">print_r</span><span style="color: rgb(0, 119, 0);">(</span><span style="color: rgb(0, 0, 187);">$lines</span><span style="color: rgb(0, 119, 0);">);</span></span></code><br /><code><span style="color: rgb(0, 0, 0);"><span style="color: rgb(0, 119, 0);"></span></span></code></blockquote><br /><br />At first glance it might seem line overkill for the task, however stepping through the code you can see the expected while loop with fgets() to read each line. The only thing we are doing at this stage is shifting the first line of the $lines array if we happen to read too many lines, and also tallying up how many characters we managed to read for each line.<br /><br />If we exit the while/fgets loop with the correct number of lines, then all is well, we can exit the main retry loop and we have the result in $lines.<br /><br />Where the code gets interesting is what we do if we don't achieve the required number of lines.&nbsp; The simple fix is to step back by a further 10% by increasing the offset and trying again, but remember we also counted up the number of bytes we read for each line we did get, so we can very simply obtain an average real-file line size by dividing this with the number of lines in our $lines array. This enables us to override the previous offset value to something larger, if indeed we were wildly off in our estimates.<br /><br />By adjusting our offset value and letting the loop repeat, the routine will try again and repeat until it succeeds or fails gracefully by the file not having sufficient lines, in which case it'll return what it could get.<br /><br />For the complete, working, source code please visit:<br /><blockquote><a href="http://pgregg.com/projects/php/code/tail-10.phps">http://pgregg.com/projects/php/code/tail-10.phps</a><br /></blockquote>Sample execution:<br /><blockquote><a href="http://pgregg.com/projects/php/code/tail-10.php">http://pgregg.com/projects/php/code/tail-10.php</a><br /></blockquote> <div><br /></div>]]>
        
    </content>
</entry>

<entry>
    <title>TinyURL PHP &quot;flaw&quot; ?</title>
    <link rel="alternate" type="text/html" href="http://pgregg.com/blog/2009/03/tinyurl-php-flaw.html" />
    <id>tag:pgregg.com,2009:/blog//2.329</id>

    <published>2009-03-19T02:17:26Z</published>
    <updated>2009-06-03T20:19:05Z</updated>

    <summary>The Register is running a story today TinyURL, your configs are showing which points out that TinyURL has a /php.php page displaying the contents of phpinfo().The article then goes on to make some scary sounding claims from security consultant Rafal Los &quot;Why would you want to run a web service as &apos;Administrator&apos; because if I figure out a way to jack that service, I completely, 100% own that machine.&quot; and &quot;More importantly... why is this server running as ROOT:WHEEL?!&quot;Sorry Rafal - but you appear to have no idea how web servers work, or all that much about (web) security.All unix...</summary>
    <author>
        <name>Paul Gregg</name>
        <uri>http://pgregg.com</uri>
    </author>
    
        <category term="PHP" scheme="http://www.sixapart.com/ns/types#category" />
    
    <category term="php" label="PHP" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="security" label="security" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="theregister" label="theregister" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en" xml:base="http://pgregg.com/blog/">
        <![CDATA[<a href="http://www.theregister.co.uk/">The Register</a> is running a story today <a href="http://www.theregister.co.uk/2009/03/18/tinyurl_misconfigured/">TinyURL, your configs are showing</a> which points out that TinyURL has a <a href="http://tinyurl.com/php.php">/php.php</a> page displaying the contents of phpinfo().<br /><br /><a href="http://preachsecurity.blogspot.com/2009/03/fox-news-fail-on-twitter.html">The article</a> then goes on to make some scary sounding claims from security consultant Rafal Los "Why would you want to run a web service as 'Administrator' because if
I figure out a way to jack that service, I completely, 100% own that
machine." and "<span>More importantly... why is this server running as <span style="font-weight: bold; font-style: italic;">ROOT:WHEEL</span>?!</span>"<br /><br />Sorry Rafal - but you appear to have no idea how web servers work, or all that much about (web) security.<br /><br />All unix based webservers start as root if they want to bind to the restricted (and default) port 80, after which they switch to the configured UID for request handling.&nbsp; So, right there, goes all Rafal's claims about pwning the machine.<br /><br />Check your own server, the _SERVER and _ENV values will reflect the
starting shell/environment, which just happens to be root.&nbsp; In
other words, there is nothing wrong with the settings. Having said that, they do have register_globals turned on, which isn't ideal - but it isn't a gaping hole if the underlying php code is safely coded.<br /><br />Also to TinyURL's credit, they are running <a href="http://www.hardened-php.net/">Suhosin patch</a> to harden their server.&nbsp; They're also running the latest production PHP (which is more than I can say).&nbsp; Granted, they probably don't want to be exposing phpinfo() - but this all just an overblown storm in a teacup.<br /><br />]]>
        
    </content>
</entry>

<entry>
    <title>PHP on LinkedIn.com</title>
    <link rel="alternate" type="text/html" href="http://pgregg.com/blog/2009/02/php-on-linkedincom.html" />
    <id>tag:pgregg.com,2009:/blog//2.324</id>

    <published>2009-02-03T23:04:30Z</published>
    <updated>2009-06-03T20:21:33Z</updated>

    <summary><![CDATA[Since LinkedIn opened up its Groups system, there has been a huge growth in the number of groups related to PHP.&nbsp; Some with charters, some without; some with a specific community background and others with a specific regional focus.&nbsp; I am posting this to bring attention to some of them.In order of popularity (member count) some general groups (non-regional) are:LinkedPHPersPhp developersPHPPHP languagePHP Technology SpecialistsPEARPHP Developer NetworkPHP User GroupSome of these are useful if you are looking for a job (the recruiters tend to play nice and stay on-topic), others ban job posts and stick to discussions.There are literally hundreds of...]]></summary>
    <author>
        <name>Paul Gregg</name>
        <uri>http://pgregg.com</uri>
    </author>
    
        <category term="PHP" scheme="http://www.sixapart.com/ns/types#category" />
    
    <category term="linkedin" label="linkedin" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="php" label="PHP" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en" xml:base="http://pgregg.com/blog/">
        <![CDATA[Since LinkedIn opened up its Groups system, there has been a huge growth in the number of groups related to PHP.&nbsp; Some with charters, some without; some with a specific community background and others with a specific regional focus.&nbsp; I am posting this to bring attention to some of them.<br /><br />In order of popularity (member count) some general groups (non-regional) are:<br /><br /><ul><li><a href="http://www.linkedin.com/groups?gid=40870">LinkedPHPers</a></li><li><a href="http://www.linkedin.com/groups?gid=42140">Php developers</a></li><li><a href="http://www.linkedin.com/groups?gid=24405">PHP</a></li><li><a href="http://www.linkedin.com/groups?gid=40895">PHP language</a></li><li><a href="http://www.linkedin.com/groups?gid=60159">PHP Technology Specialists</a></li><li><a href="http://www.linkedin.com/groups?gid=36298">PEAR</a></li><li><a href="http://www.linkedin.com/groups?gid=87251">PHP Developer Network</a></li><li><a href="http://www.linkedin.com/groups?gid=68371">PHP User Group</a></li></ul>Some of these are useful if you are looking for a job (the recruiters tend to play nice and stay on-topic), others ban job posts and stick to discussions.<br />There are literally hundreds of groups related to PHP in some shape or fashion - pure PHP, LAMP, PHP&amp;Mysql, Frameworks, and many regional *PUG type groups.<br /><br /> <div><br /></div>]]>
        
    </content>
</entry>

<entry>
    <title>To silent fanfare, Microsoft released SQL Server 2005 Driver for PHP</title>
    <link rel="alternate" type="text/html" href="http://pgregg.com/blog/2008/08/to-silent-fanfare-microsoft-released-sql-server-2005-driver-for-php.html" />
    <id>tag:pgregg.com,2008:/blog//2.322</id>

    <published>2008-08-07T15:29:11Z</published>
    <updated>2009-06-03T20:22:14Z</updated>

    <summary><![CDATA[On July 24, Microsoft released version 1.0 of their native SQL Server 2005 Driver for PHP.http://www.microsoft.com/downloads/details.aspx?FamilyId=61 ... 597C21A2E2A&amp;displaylang=enSome months back I downloaded a beta version of this after having problems working with international characters (UTF-8) with PDO and MSSQL and impressively the SQL Server 2005 Driver for PHP worked very well.Congratulations to Microsoft for continuing with this and their recent contribution to ADODB.&nbsp; I'm looking for better PDO support now...]]></summary>
    <author>
        <name>Paul Gregg</name>
        <uri>http://pgregg.com</uri>
    </author>
    
        <category term="PHP" scheme="http://www.sixapart.com/ns/types#category" />
    
    <category term="microsoft" label="microsoft" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="pdo" label="pdo" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="php" label="PHP" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="sql" label="sql" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en" xml:base="http://pgregg.com/blog/">
        <![CDATA[On July 24, Microsoft released version 1.0 of their native SQL Server 2005 Driver for PHP.<br /><br /><a href="http://www.microsoft.com/downloads/details.aspx?FamilyId=61BF87E0-D031-466B-B09A-6597C21A2E2A&amp;displaylang=en" rel="nofollow" target="_blank">http://www.microsoft.com/downloads/details.aspx?FamilyId=61 ... 597C21A2E2A&amp;displaylang=en</a><br /><br />Some months back I downloaded a beta version of this after having problems working with international characters (UTF-8) with PDO and MSSQL and impressively the SQL Server 2005 Driver for PHP worked very well.<br /><br />Congratulations to Microsoft for continuing with this and their recent contribution to ADODB.&nbsp; I'm looking for better PDO support now <img src="img/smilies/smile.png" alt=":)" width="15" height="15" />]]>
        
    </content>
</entry>

<entry>
    <title>Want to try out the next major version of Delphi for PHP ?</title>
    <link rel="alternate" type="text/html" href="http://pgregg.com/blog/2007/12/want-to-try-out-the-next-major-version-of-delphi-for-php.html" />
    <id>tag:pgregg.com,2007:/blog//2.317</id>

    <published>2007-12-19T23:10:08Z</published>
    <updated>2009-06-03T20:25:47Z</updated>

    <summary>CodeGear is looking for additional beta testers for the next major version of Delphi for PHP (http://www.codegear.com/products/delphi/php). Interested PHP developers can apply to become a field tester at http://www.surveymonkey.com/s.aspx?sm=AsT7NAxv4zC5WYKIaK9pRw_3d_3d....</summary>
    <author>
        <name>Paul Gregg</name>
        <uri>http://pgregg.com</uri>
    </author>
    
        <category term="PHP" scheme="http://www.sixapart.com/ns/types#category" />
    
    <category term="delphi" label="delphi" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="php" label="PHP" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en" xml:base="http://pgregg.com/blog/">
        <![CDATA[CodeGear is looking for additional beta testers for the next major version of Delphi for PHP (<a href="http://www.codegear.com/products/delphi/php" rel="nofollow" target="_blank">http://www.codegear.com/products/delphi/php</a>). <br />Interested PHP developers can apply to become a field tester at <a href="http://www.surveymonkey.com/s.aspx?sm=AsT7NAxv4zC5WYKIaK9pRw_3d_3d" rel="nofollow" target="_blank">http://www.surveymonkey.com/s.aspx?sm=AsT7NAxv4zC5WYKIaK9pRw_3d_3d</a>.]]>
        
    </content>
</entry>

<entry>
    <title>FastCGI for IIS (PHP related)</title>
    <link rel="alternate" type="text/html" href="http://pgregg.com/blog/2007/09/fastcgi-for-iis-php-related.html" />
    <id>tag:pgregg.com,2007:/blog//2.316</id>

    <published>2007-09-28T08:13:09Z</published>
    <updated>2009-06-03T20:24:13Z</updated>

    <summary>I haven&apos;t seen this announced on the PHP blogs (planets) yet and since it may be of interest to those running PHP (with IIS as a CGI) I&apos;ll repost the details here.Joe Stagner&apos;s Blog (Microsoft employee) has posted that the IIS team have released the FastCGI extension for IIS 5.1/6.0 as a free download from http://www.iis.net/.Rather than reword it all, here are the salient bits in Joe&apos;s words:HUGE KUDOS to the IIS team for their hard work and innovation (technical and political) for making FastCGI a reality.) If your a developer that needs to use a CGI based platform (Like...</summary>
    <author>
        <name>Paul Gregg</name>
        <uri>http://pgregg.com</uri>
    </author>
    
        <category term="PHP" scheme="http://www.sixapart.com/ns/types#category" />
    
    <category term="iis" label="IIS" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="php" label="PHP" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en" xml:base="http://pgregg.com/blog/">
        <![CDATA[I haven't seen this announced on the PHP blogs (planets) yet and since it may be of interest to those running PHP (with IIS as a CGI) I'll repost the details here.<br /><br /><a href="http://blogs.msdn.com/joestagner/archive/2007/09/27/fastcgi-for-iis-goes-live.aspx" rel="nofollow" target="_blank">Joe Stagner's Blog</a> (Microsoft employee) has posted that the IIS team have released the FastCGI extension for IIS 5.1/6.0 as a free download from <a href="http://www.iis.net/" rel="nofollow" target="_blank">http://www.iis.net/</a>.<br /><br />Rather than reword it all, here are the salient bits in Joe's words:<br /><br /><br /><table style="width: 95%;" align="center" cellpadding="6" cellspacing="4"><tbody><tr><td class="punquote"><span class="puntext">HUGE KUDOS to the IIS team for their hard work and innovation (technical and political) for making FastCGI a reality.) <br /><br />If your a developer that needs to use a CGI based platform (Like PHP) and work on Windows � then this is a godsend.<br /><br />They guys went into over-drive to get this ready before the upcoming Zend-Con.<br /><br />Here are the official particulars.<br /><br />Since early 2006, Microsoft and Zend have been working together on a technical collaboration with the PHP community to significantly enhance the reliability and performance of PHP on Windows Server 2003 and Windows Server 2008.&nbsp; As part of this collaboration, the IIS product group has been working on a new component for IIS6 and IIS7 called FastCGI Extension which will enable IIS to much more effectively host PHP applications.&nbsp; &nbsp;<br /></span></td></tr></tbody></table><span class="puntext"></span>]]>
        
    </content>
</entry>

<entry>
    <title>Delphi for PHP (aka D4PHP)</title>
    <link rel="alternate" type="text/html" href="http://pgregg.com/blog/2007/05/delphi-for-php-aka-d4php.html" />
    <id>tag:pgregg.com,2007:/blog//2.314</id>

    <published>2007-05-03T23:03:05Z</published>
    <updated>2009-02-01T22:29:08Z</updated>

    <summary><![CDATA[Yesterday I installed Delphi for PHP (D4PHP), a new development environment from CodeGear, a Borland company.D4PHP is CodeGear's first attempt at bringing visual rapid application development to PHP.&nbsp; To be honest my first impression was fear. Here I am, 10+ years into PHP and used to developing raw PHP in vi and Zend Studio, with a couple of forays into Eclipse, staring at a PHP RAD tool without the first clue where to begin.At first glance, it looks like a familiar IDE environment and my first thought was to File / Open, open a .php and use it as an...]]></summary>
    <author>
        <name>Paul Gregg</name>
        <uri>http://pgregg.com</uri>
    </author>
    
        <category term="PHP" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="en" xml:base="http://pgregg.com/blog/">
        <![CDATA[Yesterday I installed <a href="http://www.codegear.com/products/delphi/php" rel="nofollow" target="_blank">Delphi for PHP</a> (D4PHP), a new development environment from <a href="http://www.codegear.com" rel="nofollow" target="_blank">CodeGear, a Borland company</a>.<br><br>D4PHP is CodeGear's first attempt at bringing visual rapid application development to PHP.&nbsp; To be honest my first impression was fear. Here I am, 10+ years into PHP and used to developing raw PHP in vi and Zend Studio, with a couple of forays into Eclipse, staring at a PHP RAD tool without the first clue where to begin.<br><br>At first glance, it looks like a familiar IDE environment and my first thought was to File / Open, open a .php and use it as an editor. But no, if I'm going to use it I should use it the way it was meant to be.<br><br>&quot;Hello world&quot; was pretty straight forward, but going beyond that left me pretty bewildered, not sure what was going on underneath, nor how it was supposed to plug together. VCLs (Visual Component Library) exist to &quot;simplify&quot; the building of applications, so I thought I would follow the build-a-component guide and turn my <a href="http://www.pgregg.com/forums/viewtopic.php?tid=73" rel="nofollow" target="_blank">preg_find</a> recursive directory iterator/filter/sorter into a VCL.&nbsp; I think I succeeded (it &quot;compiles&quot; ok) but I don't know how to test it!<br><br>Make no mistake - D4PHP is a mindshift for traditional PHP developers like myself.&nbsp; &nbsp;<br><br>Step back from the POST handlers and sequential logic we are used to and prepare to look at application components that seem to have a life of their own.&nbsp; &nbsp;I have to confess that being a simple procedural kind of guy that thinks OO is mostly hype, D4PHP is a huge concept for me to grapple with.&nbsp; But I think I might just enjoy it.]]>
        
    </content>
</entry>

<entry>
    <title>Code release: preg_find() - A recursive file listing tool for PHP</title>
    <link rel="alternate" type="text/html" href="http://pgregg.com/blog/2007/04/code-release-preg-find---a-recursive-file-listing-tool-for-php.html" />
    <id>tag:pgregg.com,2007:/blog//2.313</id>

    <published>2007-04-18T22:48:43Z</published>
    <updated>2009-06-03T20:23:35Z</updated>

    <summary><![CDATA[Version 2.1I originally wrote this a few years ago and never really promoted it beyond the realms of the #php IRC channel on EfNet.&nbsp; However, it has managed to find its way into applications such as WordPress and many other PHP apps.&nbsp; It is gratifying to know that others are finding it useful.So what is preg_find() anyway? A short summary for those who have never encountered it: Imaging a recursive capable glob() with the ability to filter the results with a regex (PCRE) and various arguments to modify the results to bring back additional data. Well today I thought I...]]></summary>
    <author>
        <name>Paul Gregg</name>
        <uri>http://pgregg.com</uri>
    </author>
    
        <category term="PHP" scheme="http://www.sixapart.com/ns/types#category" />
    
    <category term="algorithms" label="algorithms" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="php" label="PHP" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="preg_find" label="preg_find" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en" xml:base="http://pgregg.com/blog/">
        <![CDATA[Version 2.1<br /><br />I originally wrote this a few years ago and never really promoted it beyond the realms of the #php IRC channel on EfNet.&nbsp; However, it has managed to find its way into applications such as WordPress and many other PHP apps.&nbsp; It is gratifying to know that others are finding it useful.<br /><br />So what is preg_find() anyway? A short summary for those who have never encountered it: Imaging a recursive capable glob() with the ability to filter the results with a regex (PCRE) and various arguments to modify the results to bring back additional data.<br /> <br />Well today I thought I would add one commonly requested feature. Sorting.&nbsp; Using the power of PHP's anonymous (lambda-style) functions, preg_find() now creates a custom sort routine based on the arguments passed in, filename, dir+filename, last modified, file size, disk usage (yes those last 2 are different) in either ascending or decending order.<br /><br /><a href="http://www.pgregg.com/projects/php/preg_find/preg_find.phps" rel="nofollow" target="_blank">Download preg_find.phps</a><br /><a href="http://www.pgregg.com/projects/php/preg_find/preg_find.php.txt" rel="nofollow" target="_blank">Download preg_find.php in plain text format</a><br /><br /><br />A simple example to get started - we'll work on my PHP miscellaneous <a href="http://www.pgregg.com/projects/php/code" rel="nofollow" target="_blank">code</a> directory:<br /><br />Example 1: List the files (no directories):<br /><br /><table style="width: 95%;" align="center" cellpadding="6" cellspacing="4"><tbody><tr><td class="punquote"><span class="puntext"><b>Code:</b></span><br /><br /><pre>include 'preg_find.php';
$files = preg_find('/./', '../code');
foreach($files as $file) printf("&lt;br&gt;%s\n", $file);</pre></td></tr></tbody></table><span class="puntext"><br />You can <a href="http://www.pgregg.com/projects/php/preg_find/preg_find_ex.php#1" rel="nofollow" target="_blank">see the result here</a><br /><br /><br />Now let us look at a recursive search - this is easy, just pass in the PREG_FIND_RECURSIVE argument.<br />Example 2: List the files, recursively:<br /><br /></span><table style="width: 95%;" align="center" cellpadding="6" cellspacing="4"><tbody><tr><td class="punquote"><span class="puntext"><b>Code:</b></span><br /><br /><pre>$files = preg_find('/./', '../code', PREG_FIND_RECURSIVE);
foreach($files as $file) printf("&lt;br&gt;%s\n", $file);</pre></td></tr></tbody></table><span class="puntext"><br />You can <a href="http://www.pgregg.com/projects/php/preg_find/preg_find_ex.php#2" rel="nofollow" target="_blank">see the result here</a><br /><br /><br />Lets go futher, this time we don't want to see any files - only a directory structure.<br />Example 3: List the directory tree:<br /><br /></span><table style="width: 95%;" align="center" cellpadding="6" cellspacing="4"><tbody><tr><td class="punquote"><span class="puntext"><b>Code:</b></span><br /><br /><pre>$files = preg_find('/./', '../code', PREG_FIND_DIRONLY|PREG_FIND_RECURSIVE);
foreach($files as $file) printf("&lt;br&gt;%s\n", $file);</pre></td></tr></tbody></table><span class="puntext"><br />You can <a href="http://www.pgregg.com/projects/php/preg_find/preg_find_ex.php#3" rel="nofollow" target="_blank">see the result here</a><br /><br />It should be obvious by now that we are using constants as our modifier arguments. What might not be immediately obvious is that these constants are "bit" values (.e.g. 1, 2, 4, 8, 16, ..., 1024, etc) and using PHP's <strong>Bitwise Or</strong> operator "<strong>|</strong>" we can combine modifiers to pass multiple modifiers into the function.<br /><br /><br />How about a regex? Files starting with str_ and ending in .php<br />Example 4: Using a regex on the same code as example 1:<br /><br /></span><table style="width: 95%;" align="center" cellpadding="6" cellspacing="4"><tbody><tr><td class="punquote"><span class="puntext"><b>Code:</b></span><br /><br /><pre>$files = preg_find('/^str_.*?\.php$/D', '../code');
foreach($files as $file) printf("&lt;br&gt;%s\n", $file);</pre></td></tr></tbody></table><span class="puntext"><br />You can <a href="http://www.pgregg.com/projects/php/preg_find/preg_find_ex.php#4" rel="nofollow" target="_blank">see the result here</a><br /><br /><br />What about that funky PREG_FIND_RETURNASSOC modifier?<br />This will change the output dramatically from a simple file/directory array to an associative array where the key is the filename, and the value is lots of information about that file.<br /><br />Example5: Use of PREG_FIND_RETURNASSOC<br /><br /></span><table style="width: 95%;" align="center" cellpadding="6" cellspacing="4"><tbody><tr><td class="punquote"><span class="puntext"><b>Code:</b></span><br /><br /><pre>$files = preg_find('/^str_.*?\.php$/D', '../code', PREG_FIND_RETURNASSOC);
foreach($files as $file) printf("&lt;br&gt;%s\n", $file);</pre></td></tr></tbody></table><span class="puntext"><br />You can <a href="http://www.pgregg.com/projects/php/preg_find/preg_find_ex.php#5" rel="nofollow" target="_blank">see the result here</a><br /><br /><br />As I mentioned earlier, I added sorting capability to the results, so let us look at some examples of that.<br /><br />Example 6. Sorting the results (of example 1)<br /><br /></span><table style="width: 95%;" align="center" cellpadding="6" cellspacing="4"><tbody><tr><td class="punquote"><span class="puntext"><b>Code:</b></span><br /><br /><pre>$files = preg_find('/./', '../code', PREG_FIND_SORTKEYS);
foreach($files as $file) printf("&lt;br&gt;%s\n", $file);</pre></td></tr></tbody></table><span class="puntext"><br />You can <a href="http://www.pgregg.com/projects/php/preg_find/preg_find_ex.php#6" rel="nofollow" target="_blank">see the result here</a><br /><br /><br />Example 7. And reverse sort.<br /><br /></span><table style="width: 95%;" align="center" cellpadding="6" cellspacing="4"><tbody><tr><td class="punquote"><span class="puntext"><b>Code:</b></span><br /><br /><pre>$files = preg_find('/./', '../code', PREG_FIND_SORTKEYS|PREG_FIND_SORTDESC);
foreach($files as $file) printf("&lt;br&gt;%s\n", $file);</pre></td></tr></tbody></table><span class="puntext"><br />You can <a href="http://www.pgregg.com/projects/php/preg_find/preg_find_ex.php#7" rel="nofollow" target="_blank">see the result here</a><br /><br /><br />Ok, thats all well and good, what about something more interesting?<br /><br />Example 8. Finding the largest 5 files in the tree, sorted by filesize, descending.<br /><br /></span><table style="width: 95%;" align="center" cellpadding="6" cellspacing="4"><tbody><tr><td class="punquote"><span class="puntext"><b>Code:</b></span><br /><br /><pre>$files = preg_find('/./', '../code',
  PREG_FIND_RECURSIVE|PREG_FIND_RETURNASSOC|PREG_FIND_SORTFILESIZE|PREG_FIND_SORTDESC);
$i=1;
foreach($files as $file =&gt; $stats) {
  printf('&lt;br&gt;%d) %d %s', $i, $stats['stat']['size'], $file);
  $i++;
  if ($i &gt; 5) break;
}</pre></td></tr></tbody></table><span class="puntext"><br />You can <a href="http://www.pgregg.com/projects/php/preg_find/preg_find_ex.php#8" rel="nofollow" target="_blank">see the result here</a>.<br /><br />Or what about the 10 most recently modified files?<br /><br />Example 9.<br /><br /></span><table style="width: 95%;" align="center" cellpadding="6" cellspacing="4"><tbody><tr><td class="punquote"><span class="puntext"><b>Code:</b></span><br /><br /><pre>$files = preg_find('/./', '../code',
  PREG_FIND_RECURSIVE|PREG_FIND_RETURNASSOC|PREG_FIND_SORTMODIFIED|PREG_FIND_SORTDESC);
$i=1;
foreach($files as $file =&gt; $stats) {
  printf('&lt;br&gt;%d) %s - %d bytes - %s', $i,
    date('Y-m-d H:i:s', $stats['stat']['mtime']), $stats['stat']['size'], $file);
  $i++;
  if ($i &gt; 10) break;
}</pre></td></tr></tbody></table><span class="puntext"><br />You can <a href="http://www.pgregg.com/projects/php/preg_find/preg_find_ex.php#9" rel="nofollow" target="_blank">see the result here</a>.<br /><br />I am keen to receive feedback on what you think of this function.&nbsp; &nbsp;If you have used it in some other application - great, I would love to know.&nbsp; Suggestions, improvements, criticisms are also always welcome.</span>]]>
        
    </content>
</entry>

<entry>
    <title>Compiling PHP, OCI8 on Sparc64 Solaris 10 with Oracle10g</title>
    <link rel="alternate" type="text/html" href="http://pgregg.com/blog/2005/09/compiling-php-oci8-on-sparc64-solaris-10-with-oracle10g.html" />
    <id>tag:pgregg.com,2005:/blog//2.294</id>

    <published>2005-09-27T09:53:18Z</published>
    <updated>2009-02-01T22:29:01Z</updated>

    <summary><![CDATA[This problem beat me about the head for most of yesterday until I worked out that PHP 5.0.5 doesn't actually know about Oracle 10.&nbsp; &nbsp; 8 and 9, sure thing - otherwise it decides it is an older version (very silly).The other problem is that when PHP tries to link to the oracle client libraries, by default it attempts to link against the 64 bit libraries - which with PHP being a 32bit app just isn't going to fly.So here I will attempt to guide you in all that is good with PHP and Oracle 10.The first thing to do...]]></summary>
    <author>
        <name>Paul Gregg</name>
        <uri>http://pgregg.com</uri>
    </author>
    
        <category term="PHP" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="en" xml:base="http://pgregg.com/blog/">
        <![CDATA[This problem beat me about the head for most of yesterday until I worked out that PHP 5.0.5 doesn't actually know about Oracle 10.&nbsp; &nbsp; 8 and 9, sure thing - otherwise it decides it is an older version (very silly).<br><br>The other problem is that when PHP tries to link to the oracle client libraries, by default it attempts to link against the 64 bit libraries - which with PHP being a 32bit app just isn't going to fly.<br><br>So here I will attempt to guide you in all that is good with PHP and Oracle 10.<br><br><br>The first thing to do is ensure you have a working Solaris 10 install with Oracle 10g already<br>installed.&nbsp; &nbsp;As this was to be an actual server machine I installed the full database server including client libraries (which happens by default when you install server).&nbsp; However the purpose of this is not to help you install Oracle - there are plenty of guides out there for that.&nbsp; This is to help you get PHP compiled in this environment - there are no guides for that.<br><br>So lets unpack the PHP source:<br>#-&gt;tar xf php-5.0.5.tar<br>#-&gt;cd php-5.0.5<br>php-5.0.5-#-&gt;<br><br>Now, If you run a straight ./configure --with-oci8 it will most likely fail being unable to find the oracle install:<br>checking Oracle version... configure: error: Oracle (OCI8) required libraries not found<br><br>We need to tell it where to find the oracle libraries.<br>./configure --with-oci8=/u01/app/oracle/product/10.2.0/Db_1<br>(assuming this is where your default database was installed to)<br><br>This will enable configure to complete.<br><br>Next, naturally, we try to make php - all should go well right up until the final link:<br>php-5.0.5-#-&gt;make<br>... [snip] ...<br>ld: fatal: file /u01/app/oracle/product/10.2.0/Db_1/lib/libclntsh.so: wrong ELF class: ELFCLASS64<br>ld: fatal: File processing errors. No output written to sapi/cgi/php<br>collect2: ld returned 1 exit status<br>make: *** [sapi/cgi/php] Error 1<br><br>This fails because PHP has decided to link against lib/libclntsh.so when it should have linked against lib32/libclntsh.so<br><br>No amount of adding --includedir= and --libdir= on the configure command will result in make doing the right thing and linking against the lib32 version.<br><br>The solution? We need to edit the configure script to tell it that lib isn't the be-all and end-all of oracle libraries.&nbsp; This is a pain, I know, but hopefully the PHP people will fix this for 5.0.6 and above.<br><br>At line 64660 in configure you will see the line:<br>&nbsp; elif test -f $OCI8_DIR/lib/libclntsh.$SHLIB_SUFFIX_NAME.10.1; then<br><br>Change /lib/ to /lib32/<br><br>And at line 69134 you'll notice that it is missing any reference to Oracle 10.1, so we need to add it - add the following two lines just before the 9.0 line:<br>&nbsp; elif test -f $ORACLE_DIR/lib32/libclntsh.$SHLIB_SUFFIX_NAME.10.1; then<br>&nbsp; &nbsp; ORACLE_VERSION=10.1<br><br>At line 64977 change:<br>&nbsp; if test -z &quot;$OCI8_DIR/lib&quot; || echo &quot;$OCI8_DIR/lib&quot; | grep '^/' &gt;/dev/null ; then<br>&nbsp; &nbsp; ai_p=$OCI8_DIR/lib<br>to:<br>&nbsp; if test -z &quot;$OCI8_DIR/lib32&quot; || echo &quot;$OCI8_DIR/lib32&quot; | grep '^/' &gt;/dev/null ; then<br>&nbsp; &nbsp; ai_p=$OCI8_DIR/lib32<br><br>Line 64368: add<br>&nbsp; OCI8_SHARED_LIBADD=&quot;-L$OCI8_DIR/lib32&quot;<br>&nbsp; LIBS=&quot;$LIBS -L$OCI8_DIR/lib32&quot;<br><br><br>Now make clean;<br>cd to your database and rename the lib directory to lib.unused temporarily so that PHP cannot link against it and leave the lib32 one as is.<br><br>Switch back to php dir. Run your configure command, make (which should now complete) and make install.<br><br>Go back and rename the lib.unused back to lib as other things will need this to exist.<br><br>Finally, make sure you add the lib32 path to your LD_LIBRARY_PATH variable before starting apache/php<br><br>LD_LIBRARY_PATH=&quot;$LD_LIBRARY_PATH:/u01/app/oracle/product/10.2.0/Db_1/lib32&quot;<br><br>Your PHP should now be working fine.<br><br><br>Files to help: My &quot;configure&quot; command:<br>'./configure' \<br>'--prefix=/usr/local/apache2' \<br>'--includedir=/space/app/oracle/product/10.2.0/Db_1/rdbms/public' \<br>'--oldincludedir=/space/app/oracle/product/10.2.0/Db_1/rdbms/public' \<br>'--libdir=/space/app/oracle/product/10.2.0/Db_1/lib32' \<br>'--with-apxs2=/usr/local/apache2/bin/apxs' \<br>'--with-oci8=/u01/app/oracle/product/10.2.0/Db_1'<br><br>Diff of the configure script to the regular one supplied with PHP 5.0.5<br>#-&gt;diff php-5.0.5/configure php-5.0.5-working/configure&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;6:39AM<br>64367a64368,64369<br>&gt;&nbsp; &nbsp;OCI8_SHARED_LIBADD=&quot;-L$OCI8_DIR/lib32&quot;<br>&gt;&nbsp; &nbsp;LIBS=&quot;$LIBS -L$OCI8_DIR/lib32&quot;<br>64660c64662<br>&lt;&nbsp; &nbsp;elif test -f $OCI8_DIR/lib/libclntsh.$SHLIB_SUFFIX_NAME.10.1; then<br>---<br>&gt;&nbsp; &nbsp;elif test -f $OCI8_DIR/lib32/libclntsh.$SHLIB_SUFFIX_NAME.10.1; then<br>64977,64978c64979,64980<br>&lt;&nbsp; &nbsp;if test -z &quot;$OCI8_DIR/lib&quot; || echo &quot;$OCI8_DIR/lib&quot; | grep '^/' &gt;/dev/null ; then<br>&lt;&nbsp; &nbsp; &nbsp;ai_p=$OCI8_DIR/lib<br>---<br>&gt;&nbsp; &nbsp;if test -z &quot;$OCI8_DIR/lib32&quot; || echo &quot;$OCI8_DIR/lib32&quot; | grep '^/' &gt;/dev/null ; then<br>&gt;&nbsp; &nbsp; &nbsp;ai_p=$OCI8_DIR/lib32<br>69133a69136,69137<br>&gt;&nbsp; &nbsp;elif test -f $ORACLE_DIR/lib32/libclntsh.$SHLIB_SUFFIX_NAME.10.1; then<br>&gt;&nbsp; &nbsp; &nbsp;ORACLE_VERSION=10.1<br><br><br>Note to PHP developers if they read this - this patch is not one that can be dropped into the regular build - it will only help people who have difficulty installing PHP with OCI8/Oracle10 on Solaris10.<br><br>I hope this proves useful to others - it took me &gt;24 hours work to get to this point.]]>
        
    </content>
</entry>

<entry>
    <title>PHP: HTTP Authentication via PHP</title>
    <link rel="alternate" type="text/html" href="http://pgregg.com/blog/2005/09/php-http-authentication-via-php.html" />
    <id>tag:pgregg.com,2005:/blog//2.292</id>

    <published>2005-09-15T10:56:52Z</published>
    <updated>2009-02-01T22:29:00Z</updated>

    <summary><![CDATA[When combining sessions with HTTP Auth in order to maintain state. The difficulty surrounding HTTP Auth is that even after you &quot;logout&quot;, the browser will continue to send the correct username and password with each request. Thus immediately logging you back in again - unless you use the states to keep track carefully.In this example we will use two session variables to maintain state and we tell the page that we want to login our logout via an argument in the query string, e.g. ?login ?logoutThe two state variables are:&nbsp; &nbsp; * LOGGEDIN - Very simple state - either you...]]></summary>
    <author>
        <name>Paul Gregg</name>
        <uri>http://pgregg.com</uri>
    </author>
    
        <category term="PHP" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="en" xml:base="http://pgregg.com/blog/">
        <![CDATA[When combining sessions with HTTP Auth in order to maintain state. The difficulty surrounding HTTP Auth is that even after you &quot;logout&quot;, the browser will continue to send the correct username and password with each request. Thus immediately logging you back in again - unless you use the states to keep track carefully.<br><br>In this example we will use two session variables to maintain state and we tell the page that we want to login our logout via an argument in the query string, e.g. ?login ?logout<br><br>The two state variables are:<br><br>&nbsp; &nbsp; * LOGGEDIN - Very simple state - either you are logged in or not<br>&nbsp; &nbsp; * LOGGEDOUT - Will be TRUE if we have logged out. It's primary purpose is to scupper the browser provided password and prevent the authentication routines from running. It gets reset to FALSE when we want to login <br><br>Additional benefits to this method are that we only need to authenticate upon login once. Normal code implemented HTTP Auth routines authenticate with every page request<br><br>Source code: <a href="http://www.pgregg.com/projects/php/httpauth/auth.phps" rel="nofollow" target="_blank">Example page protected with PHP HTTP Auth</a><br><br>Source code: <a href="http://www.pgregg.com/projects/php/httpauth/auth.inc.phps" rel="nofollow" target="_blank">PHP HTTP Auth include file</a><br><br>In order to use this to protect any page you need to copy the auth.inc.php file to your server and then simply include or require it in any page.<br>You may wish to set the variable $HTTP_AUTH_REALM to a string before including this as this will change the Basic Realm information in the auth dialog box to a string of your choice.<br><br>You should also look at the checkpw() function and replace that with something that will check your user credentials correctly. Input is username, password and it should return TRUE or FALSE if the credentials supplied are OK or not.<br><br>Finally, on any page to effect a change of state from logged in to logged out or vice-versa, you simply have to make a link to a page with &quot;login&quot; or &quot;logout&quot; in the url's Query String (that is the bit after the ?), e.g. page.php?login<br><br>A working example is <a href="http://www.pgregg.com/projects/php/httpauth/" rel="nofollow" target="_blank">provided over in my projects section</a>, the default username is &quot;paul&quot; and password is &quot;gregg&quot;<br><br>I hope this code serves as useful learning material. Good luck.]]>
        
    </content>
</entry>

<entry>
    <title>String Case Conversion in PHP</title>
    <link rel="alternate" type="text/html" href="http://pgregg.com/blog/2005/08/string-case-conversion-in-php.html" />
    <id>tag:pgregg.com,2005:/blog//2.289</id>

    <published>2005-08-18T23:50:21Z</published>
    <updated>2009-02-01T22:29:00Z</updated>

    <summary><![CDATA[Occasionally I read through some comments on the PHP Manual, sometimes to get ideas on different methods of doing things, other times just to try to keep current with some of the vast array of functions available.Sometimes, I see things that really scare me - code that is written and published with the best will in the world from the author - but yet displays a lack of a deeper understanding of how to solve a problem.&nbsp; One such case was the invert_case() and rand_case() functions which basically looped through each character in a string doing whatever it had to...]]></summary>
    <author>
        <name>Paul Gregg</name>
        <uri>http://pgregg.com</uri>
    </author>
    
        <category term="PHP" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="en" xml:base="http://pgregg.com/blog/">
        <![CDATA[Occasionally I read through some comments on the <a href="http://www.php.net/manual/en/index.php" rel="nofollow" target="_blank">PHP Manual</a>, sometimes to get ideas on different methods of doing things, other times just to try to keep current with some of the vast array of functions available.<br><br>Sometimes, I see things that really scare me - code that is written and published with the best will in the world from the author - but yet displays a lack of a deeper understanding of how to solve a problem.&nbsp; One such case was the <a href="http://uk.php.net/manual/en/function.str-shuffle.php#54381" rel="nofollow" target="_blank">invert_case() and rand_case()</a> functions which basically looped through each character in a string doing whatever it had to do to each character as it went.&nbsp; Highly inefficient.<br><br>Remember, the only difference in ASCII between an uppercase letter and a lowercase letter is a single bit that is 0 for uppercase and 1 for lowercase.<br><br><br>This brief tutorial is based on code available at:<br><a href="http://www.pgregg.com/projects/php/code/str_case.phps" rel="nofollow" target="_blank">http://www.pgregg.com/projects/php/code/str_case.phps</a><br>and you can see example output at:<br><a href="http://www.pgregg.com/projects/php/code/str_case.php" rel="nofollow" target="_blank">http://www.pgregg.com/projects/php/code/str_case.php</a><br><br>Surely it would be possible to write some code that would simply flip this bit in each character to the value you want:<br> - AND with 0 to force uppercase<br> - OR with 1 to force lowercase<br> - XOR with 1 to invert the case<br> - randomly set it to 1 or 0 to set random case.<br><br><br>There are two methods to achieving this, the first makes a simple character mask and performs a bitwise operation on the string as a whole to change it as required.&nbsp; This method is designed to help teach how this works.&nbsp; The second method uses the power of the PCRE engine by using a regex to calculate the changes and apply them in one simple step.<br><br>Both solutions are, I believe, elegant and are presented here for you.<br><br>Solution #1:<br><br></span><table style="width: 95%" align="center" cellspacing="4" cellpadding="6"><tr><td class="punquote"><span class="puntext"><b>Code:</b></span><br><br><pre>// Code that will invert the case of every character in $input
    // The solution is to flip the value of 3rd bit in each character
    // if the character is a letter. This is done with XOR against a space (hex 32)
    $stringmask = preg_replace(&quot;/[^a-z]/i&quot;, chr(0), $input); // replace nonstrings with NULL
    $stringmask = preg_replace(&quot;/[a-z]/i&quot;, ' ', $stringmask); // replace strings with space
    return $input ^ $stringmask;</pre></td></tr></table><span class="puntext"><br>The method here is to generate a string mask, in two stages, that will act as a bitmask to XOR the 3rd bit of every letter in the string.&nbsp; Stage 1 is to replace all non-letters will a NULL byte (all zeros) and Stage 2 is to replace all letters with a space (ASCII 32) which just happens to be a byte with just the 3rd bit set to 1 i.e. 00100000<br>All we have to do then is XOR our input with the string mask and magically the case of all letters in the entire string are flipped.<br><br><br>Solution #2:<br><br></span><table style="width: 95%" align="center" cellspacing="4" cellpadding="6"><tr><td class="punquote"><span class="puntext"><b>Code:</b></span><br><br><pre>return preg_replace('/[a-z]+/ie', '\'$0\' ^ str_pad(\'\', strlen(\'$0\'), \' \')', $input);</pre></td></tr></table><span class="puntext"><br>Much more compact and works by using a regex looking for letters and using the i (case insensitive) modifier and most importantly the e (evaluate) modifier so we can replace by executing php code.&nbsp; In this case, we look for batches of letters and replace them with itself XORed with a string of spaces (of the same length).<br><br><br>Similar principles apply to the random case example, but we complicate this slightly by adding and invert mask (to the solution 1 method). This invert mask is created by taking a random amount of spaces (between 0 and the size of the input string). We then pad this out to the size of the original string with NULL bytes and finally randomise the order with str_shuffle().&nbsp; We then bitwise AND the stringmask and the invertmask so we create a new mask where randomly letters in the mask have spaces or NULLs.&nbsp; We then XOR this to the original string as before and before you know it you have a randomly capitalised string.<br>The Solution 2 version requires you to remove the + so that we only match a single letter at a time (or else our randomly chosen case would apply to words at a time), and we use a termary to randomly decide on using a space or a NULL:<br><br></span><table style="width: 95%" align="center" cellspacing="4" cellpadding="6"><tr><td class="punquote"><span class="puntext"><b>Code:</b></span><br><br><pre>return preg_replace('/[a-z]/ie', '(rand(0,1) ? \'$0\' ^ \' \' : \'$0\')', $input);</pre></td></tr></table><span class="puntext"><br>I hope this has been a worthwhile read and I would certainly welcome feedback on this article.]]>
        
    </content>
</entry>

</feed>
