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

<channel>
	<title>IONCANNON &#187; php</title>
	<atom:link href="http://www.ioncannon.net/tag/php/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.ioncannon.net</link>
	<description>Thoughts on Software Development and Engineering</description>
	<lastBuildDate>Tue, 03 Jan 2012 13:59:08 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	
	<atom:link rel='hub' href='http://www.ioncannon.net/?pushpress=hub'/>
		<item>
		<title>Google OAuth for Installed Apps PHP Example</title>
		<link>http://www.ioncannon.net/programming/1443/google-oauth-for-installed-apps-php-example/</link>
		<comments>http://www.ioncannon.net/programming/1443/google-oauth-for-installed-apps-php-example/#comments</comments>
		<pubDate>Tue, 08 Feb 2011 16:53:20 +0000</pubDate>
		<dc:creator>carson</dc:creator>
				<category><![CDATA[programming]]></category>
		<category><![CDATA[google]]></category>
		<category><![CDATA[oauth]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://www.ioncannon.net/?p=1443</guid>
		<description><![CDATA[I have been working on a long needed update to the Google analytics dashboard plugin for WordPress and one of the items I had on my TODO list was using Google&#039;s OAuth login instead of the old ClientLogin. Setting OAuth up for a WordPress plugin is complicated because it isn&#039;t a hosted application and as [...]]]></description>
			<content:encoded><![CDATA[<p>I have been working on a long needed update to the <a href="http://www.ioncannon.net/projects/google-analytics-dashboard-wordpress-widget/">Google analytics dashboard plugin for WordPress</a> and one of the items I had on my TODO list was using Google&#039;s OAuth login instead of the old ClientLogin. Setting OAuth up for a WordPress plugin is complicated because it isn&#039;t a hosted application and as such I can&#039;t register it to get OAuth keys. That is where a special way of doing OAuth comes in called <a href="http://code.google.com/apis/accounts/docs/OAuthForInstalledApps.html">OAuth for installed apps</a>.</p>
<p>There seems to be a lot of general documentation on how to do OAuth but there wasn&#039;t much about using it for installed apps so what follows is an example using PHP that is basically what went into the plugin update.</p>
<p><span id="more-1443"></span></p>
<p>It helps to first know the basic steps that will make this work. From the documentation those steps are:</p>
<ol>
<li>Your application gets an unauthorized request token from Google&#039;s authorization server.</li>
<li>Google asks the user to grant you access to the required data.</li>
<li>Your application gets an authorized request token from the authorization server.</li>
<li>You exchange the authorized request token for an access token.</li>
<li>You use the access token to request data from Google&#039;s service access servers.</li>
</ol>
<p>I will reference these steps in the code that follows.</p>
<p>Before trying this example out grab the <a href="http://oauth.googlecode.com/svn/code/php/">OAuth PHP library</a>. The only file that is needed to execute the examples is OAuth.php. </p>
<p>I have split the code into two files that I named phase1.php and phase2.php. In phase1.php the resulting URL would be used to grant access to the given scope (note that I&#039;ve marked parts of the code that would need to be customized with a &#034;Customize this&#034; comment):</p>
<pre class="brush: php; title: ; notranslate">
require_once(&quot;OAuth.php&quot;);

// This is the setup for step 1
// 1. Your application gets an unauthorized request token from Google's authorization server.
$signature_method = new OAuthSignatureMethod_HMAC_SHA1();

$params = array();

// The callback is where the user comes back after authentication
$params['oauth_callback'] = 'http://example.com/phase2.php'; // Customize this

// The scope is what you are asking for access to
$params['scope'] = 'https://www.google.com/analytics/feeds/'; // Customize this

// Setting xoauth_displayname will show the given name on the authentication screen
$params['xoauth_displayname'] = 'My Test App'; // Customize this

// Set up an anonymous oauth consumer
$consumer = new OAuthConsumer('anonymous', 'anonymous', NULL);

// Set up the request for a request token
$req_req = OAuthRequest::from_consumer_and_token($consumer, NULL, 'GET', 'https://www.google.com/accounts/OAuthGetRequestToken', $params);

// Sign the request
$req_req-&gt;sign_request($signature_method, $consumer, NULL);

// Set up curl and have it get the token to use for the authenication call
$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, $req_req-&gt;to_url());

// This tells curl to return the response as one string
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 

// Run curl and grab the output and the return code
// This is the execution of step 1
$return = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);

if($http_code == 200)
{
  // If the call was good parse out the response parameters into an array
  $access_params = array();
  $param_pairs = explode('&amp;', $return);
  foreach($param_pairs as $param_pair)
  {
    if (trim($param_pair) == '') { continue; }
    list($key, $value) = explode('=', $param_pair);
    $access_params[$key] = urldecode($value);
  }

  // Print out the authentication URL that needs to be opened in a browser
  echo &quot;Open this link: https://www.google.com/accounts/OAuthAuthorizeToken?oauth_token=&quot; . urlencode($access_params['oauth_token']) . &quot;\n&quot;;

  // This is the oauth token secret that is needed for the second part
  echo &quot;\nRemember the folowing value: &quot; . $access_params['oauth_token_secret'] . &quot;\n&quot;;

  // This is where the user would perform step 2
  // 2. Google asks the user to grant you access to the required data.
}
else
{
  echo &quot;Error: $http_code and $return\n&quot;;
}
</pre>
<p>The above code performs steps 1 and 2. There are a few notes that are good to know:</p>
<ul>
<li>The OAuth consumer is set to &#034;anonymous&#034; here because this is an installed app, that is used again in the second phase.</li>
<li>There can be multiple scope values, just seperate them with a space.</li>
<li>Make sure to save the oauth_token_secret value, it is required for the second phase.</li>
</ul>
<p>At this point the user goes off to authorize the application on Google&#039;s system. When they are done they are redirected back to your callback URL specified in phase 1 as oauth_callback. I put the following code in a file named phase2.php (again I&#039;ve noted places that would need to be customized):</p>
<pre class="brush: php; title: ; notranslate">
require_once(&quot;OAuth.php&quot;);

// This is where step 3 comes in, this would be in the callback and would be called
// after the user authenticates.
// 3. Your application gets an authorized request token from the authorization server.
$oauth_verifier = urldecode($_REQUEST['oauth_verifier']);
$oauth_token = urldecode($_REQUEST['oauth_token']);

// The token secret would need to be looked up from the previous step
$ouath_token_secret = ''; // Customize this

// This is where setup for step 4 starts
// 4. You exchange the authorized request token for an access token.
$signature_method = new OAuthSignatureMethod_HMAC_SHA1();

$params = array();

$params['oauth_verifier'] = $oauth_verifier;

// Set up the anonymous consumer again
$consumer = new OAuthConsumer('anonymous', 'anonymous', NULL);

// Use the oauth token and token secret to set up the consumer for the access token
$final_consumer = new OAuthConsumer($oauth_token, $ouath_token_secret);

// Set up the call to get the access token
$acc_req = OAuthRequest::from_consumer_and_token($consumer, $final_consumer, 'GET', 'https://www.google.com/accounts/OAuthGetAccessToken', $params);

$acc_req-&gt;sign_request($signature_method, $consumer, $final_consumer);

// Set up curl and have it get the final token and secret
$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, $acc_req-&gt;to_url());

// This tells curl to return the response as one string
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 

// Run curl and grab the output and the return code
// This is the execution of step 4
$return = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);

if($http_code == 200)
{
  // If the call was good parse out the response parameters into an array
  $access_params = array();
  $param_pairs = explode('&amp;', $return);
  foreach($param_pairs as $param_pair)
  {
    if (trim($param_pair) == '') { continue; }
    list($key, $value) = explode('=', $param_pair);
    $access_params[$key] = urldecode($value);
  }

  // Print out the final information needed for access to the service that was in &quot;scope&quot;
  echo &quot;Access info: &quot; . $access_params['oauth_token'] . &quot; and &quot; . $access_params['oauth_token_secret'] . &quot;\n&quot;;
}
else
{
  echo &quot;Error: $http_code and $return\n&quot;;
}
</pre>
<p>At this point you have a good oauth_token and oauth_token_secret that would need to be stored securely. These two bits of information allow you to make calls later to services provided under the given scope listed in the initial token request. Here is an example of making a request:</p>
<pre class="brush: php; title: ; notranslate">
require_once(&quot;OAuth.php&quot;);

// This is step 5
// 5. You use the access token to request data from Google's service access servers.

$url = 'https://www.google.com/analytics/feeds/accounts/default';

$signature_method = new OAuthSignatureMethod_HMAC_SHA1();

$params = array();

$consumer = new OAuthConsumer('anonymous', 'anonymous', NULL);

// Here the saved oauth_token and oauth_token_secret are used
$token = new OAuthConsumer($saved_oauth_token, $saved_oauth_token_secret);

$oauth_req = OAuthRequest::from_consumer_and_token($consumer, $token, 'GET', $url, $params);

$oauth_req-&gt;sign_request($signature_method, $consumer, $token);

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, $url);

// Use the oauth request to create the authenication header
curl_setopt($ch, CURLOPT_HTTPHEADER, array($oauth_req-&gt;to_header())));

curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

$return = curl_exec($ch);
$this-&gt;http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);

if($this-&gt;http_code == 200)
{
  echo $return;
}
else
{
  echo $return;
}
</pre>
<p>For a lot more detail check out <a href="http://code.google.com/apis/gdata/articles/oauth.html">Google&#039;s OAuth documentation</a>. I also found it helpful to use the <a href="http://googlecodesamples.com/oauth_playground/">Google&#039;s OAuth playground</a> to test things out.</p>
<p>I also found a handy list of <a href="http://code.google.com/apis/gdata/faq.html#AuthScopes">all the define Google OAuth scopes</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ioncannon.net/programming/1443/google-oauth-for-installed-apps-php-example/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Using Cursors with PHP MySQLi and Multiple Prepared Statements</title>
		<link>http://www.ioncannon.net/programming/998/using-cursors-with-php-mysqli-and-multiple-prepared-statements/</link>
		<comments>http://www.ioncannon.net/programming/998/using-cursors-with-php-mysqli-and-multiple-prepared-statements/#comments</comments>
		<pubDate>Tue, 30 Mar 2010 11:37:41 +0000</pubDate>
		<dc:creator>carson</dc:creator>
				<category><![CDATA[programming]]></category>
		<category><![CDATA[cursor]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[mysqli]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://www.ioncannon.net/?p=998</guid>
		<description><![CDATA[After my post on using PHP MySQLi and multiple prepared statements at the same time someone commented that using cursors could do the same thing. With that comment I dug some more and found that modifying the cursor type that is used under the covers will indeed let you execute multiple prepared statements concurrently on [...]]]></description>
			<content:encoded><![CDATA[<p>After my post on <a href="http://www.ioncannon.net/programming/889/php-mysqli-and-multiple-prepared-statements/">using PHP MySQLi and multiple prepared statements at the same time</a> someone commented that using <a href="http://dev.mysql.com/doc/refman/5.5/en/cursors.html">cursors</a> could do the same thing. With that comment I dug some more and found that modifying the cursor type that is used under the covers will indeed let you execute multiple prepared statements concurrently on the same connection.</p>
<p>First off you may ask yourself why you would want to use this. The best answer I have for that is that the solution in the other post loads the entire result set into memory from the very start while with this solution you can control just how many rows you load. To get started you will want to take a look at the <a href="http://php.net/manual/en/mysqli-stmt.attr-set.php">MySQLi statement set attribute</a> call. This call is will let you modify the underlying cursor type that is used with the prepared statement in two ways that are useful for this issue.</p>
<p><span id="more-998"></span></p>
<p>The MYSQLI_STMT_ATTR_CURSOR_TYPE attribute controls the type of cursor used for the results from the prepared statement. The default option is MYSQLI_CURSOR_TYPE_NO_CURSOR  which results in no cursor being used at all. The only other option is MYSQLI_CURSOR_TYPE_READ_ONLY and this is the one you will want to use.</p>
<p>The MYSQLI_STMT_ATTR_PREFETCH_ROWS attribute controls the number of rows that are fetched under the covers from the server as more data is needed. The default for this attribute is 1 which means that each fetch of the row causes a round trip to the server. Larger numbers for this attribute will cause more memory to be used to store the resulting rows but fewer round trips to the server. This is the attribute that gives this option more flexibility.</p>
<p>The following is a slightly modified example that was used in the previous post now using both the MYSQLI_STMT_ATTR_CURSOR_TYPE and MYSQLI_STMT_ATTR_PREFETCH_ROWS attributes to efficiently retrieve rows from two different prepared statements at the same time:</p>
<pre class="brush: php; title: ; notranslate">
&lt;?php

  $db_connection = new mysqli('127.0.0.1', 'user', '', 'test');

  $post_stmt = $db_connection-&gt;prepare(&quot;select id, title from post where id between 10000 and 10999&quot;);
  $post_stmt-&gt;attr_set(MYSQLI_STMT_ATTR_CURSOR_TYPE, MYSQLI_CURSOR_TYPE_READ_ONLY);
  $post_stmt-&gt;attr_set(MYSQLI_STMT_ATTR_PREFETCH_ROWS, 100);
  $comment_stmt = $db_connection-&gt;prepare(&quot;select user_id from comment where post_id = ?&quot;);

  if ($post_stmt-&gt;execute())
  {
    $post_stmt-&gt;bind_result($post_id, $post_title);

    while ($post_stmt-&gt;fetch())
    {
      $comments = array();

      $comment_stmt-&gt;bind_param('i', $post_id);
      if ($comment_stmt-&gt;execute())
      {
        $comment_stmt-&gt;bind_result($user_id);
        while ($comment_stmt-&gt;fetch())
        {
          array_push($comments, array('user_id' =&gt; $user_id));
        }
      }
      else
      {
        printf(&quot;Comment statement error: %s\n&quot;, $comment_stmt-&gt;error);
      }

      printf(&quot;ID: %d -&gt; %s\n&quot;, $post_id, $post_title);
      print_r($comments);
    }
  }
  else
  {
    printf(&quot;Post statement error: %s\n&quot;, $post_stmt-&gt;error);
  }

  $post_stmt-&gt;close();
  $comment_stmt-&gt;close();

  $db_connection-&gt;close();
?&gt;
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.ioncannon.net/programming/998/using-cursors-with-php-mysqli-and-multiple-prepared-statements/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Building HipHop PHP for Fedora 12 on 64 bit and 32 bit Systems</title>
		<link>http://www.ioncannon.net/programming/918/building-hiphop-php-for-fedora-12-on-64-bit-and-32-bit-systems/</link>
		<comments>http://www.ioncannon.net/programming/918/building-hiphop-php-for-fedora-12-on-64-bit-and-32-bit-systems/#comments</comments>
		<pubDate>Tue, 23 Feb 2010 11:14:04 +0000</pubDate>
		<dc:creator>carson</dc:creator>
				<category><![CDATA[programming]]></category>
		<category><![CDATA[ec2]]></category>
		<category><![CDATA[Fedora]]></category>
		<category><![CDATA[hphp]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://www.ioncannon.net/?p=918</guid>
		<description><![CDATA[Now that Facebook has finally released the source for HipHop PHP it is time to give it a spin. Of course it is still a little rough around the edges so I figured I would toss together a quick howto on getting it to build. The first thing to note is that they are only [...]]]></description>
			<content:encoded><![CDATA[<p>Now that Facebook has finally released the source for <a href="http://github.com/facebook/hiphop-php/">HipHop PHP</a> it is time to give it a spin. Of course it is still a little rough around the edges so I figured I would toss together a quick howto on getting it to build.</p>
<p>The first thing to note is that they are only supporting 64 bit systems officially. Having said that it isn&#039;t too hard to modify the code to make it work on a 32 bit system although it may turn out that such early modifications are missing some fundamental bits on why they were only support 64 bit systems. I&#039;m going to assume at first that you are using a 64 bit system and then end with what you need if you are still using a 32 bit system.</p>
<p><span id="more-918"></span></p>
<p>I don&#039;t actually have a 64 bit system myself so I used an EC2 instance for the following instructions. To do the same start with Amazon&#039;s Basic 64-bit Fedora Core 8 (AMI Id: ami-86db39ef) instance (note that this is EBS backed so you will end up with an EBS volume after you start it) and then upgrade to Fedora 12 using my <a href="http://www.ioncannon.net/system-administration/894/fedora-12-bootable-root-ebs-on-ec2/">previous instructions on building a EBS bootable Fedora 12 instance</a>. You will need to remove a few packages to get the 64 bit version of Fedora 8 to upgrade that I didn&#039;t have to do for the 32 bit version, here are all the commands you need to get to a running 64 bit Fedora 12 instance (the entire upgrade takes about 20 minutes):</p>
<pre class="brush: bash; title: ; notranslate">
# Fedora 8 to Fedora 10
yum -y remove dmraid-1.0.0.rc14-4.fc8.i386 dmraid-1.0.0.rc14-4.fc8.i386 curl-7.18.2-7.fc8.i386
yum clean all
rpm -Uhv http://archive.kernel.org/fedora-archive/releases/10/Fedora/i386/os/Packages/fedora-release-10-1.noarch.rpm http://archive.kernel.org/fedora-archive/releases/10/Fedora/i386/os/Packages/fedora-release-notes-10.0.0-1.noarch.rpm
yum -y update

# Fedora 10 to Fedora 11
yum -y remove gpm-1.20.5-2.fc10.i386
yum clean all
rpm -Uvh http://mirrors.usc.edu/pub/linux/distributions/fedora/linux/releases/11/Fedora/i386/os/Packages/fedora-release-11-1.noarch.rpm http://mirrors.usc.edu/pub/linux/distributions/fedora/linux/releases/11/Fedora/i386/os/Packages/fedora-release-notes-11.0.0-2.fc11.noarch.rpm
yum -y update

# Fedora 11 to Fedora 12
yum -y remove cryptsetup-luks-1.0.6-7.fc11.i586
yum clean all
rpm -Uvh http://mirrors.kernel.org/fedora/releases/12/Fedora/i386/os/Packages/fedora-release-notes-12.0.0-4.fc12.noarch.rpm http://mirrors.kernel.org/fedora/releases/12/Fedora/i386/os/Packages/fedora-release-12-1.noarch.rpm
yum -y update

# Make sure the basics are installed
yum -y install gcc-c++ git
</pre>
<p>To start with there are some prerequisites you need. This can be taken care of in one command with yum:</p>
<pre class="brush: bash; title: ; notranslate">
yum -y install git cmake boost pcre-devel libicu-devel libmcrypt-devel oniguruma-devel mysql-devel gd-devel boost-devel libxml2-devel libcap-devel binutils-devel flex bison expat-devel
</pre>
<p>Next create a directory to hold everything in, change into that directory and create another directory to hold the customized libraries needed to compile HipHop PHP:</p>
<pre class="brush: bash; title: ; notranslate">
mkdir hiphop
cd hiphop
mkdir local
</pre>
<p>Next it is time to pull down the HipHop PHP source along with the source for some libraries it depends on (these all go into the hiphop directory created above):</p>
<pre class="brush: bash; title: ; notranslate">
git clone git://github.com/facebook/hiphop-php.git

wget &quot;http://downloads.sourceforge.net/project/re2c/re2c/0.13.5/re2c-0.13.5.tar.gz?use_mirror=cdnetworks-us-2&quot;
wget &quot;http://www.threadingbuildingblocks.org/uploads/77/142/2.2/tbb22_20090809oss_src.tgz&quot;
wget http://curl.haxx.se/download/curl-7.20.0.tar.bz2
wget http://www.monkey.org/~provos/libevent-1.4.13-stable.tar.gz

tar xvjf curl-7.20.0.tar.bz2
tar xvzf libevent-1.4.13-stable.tar.gz
tar xvzf re2c-0.13.5.tar.gz
tar xvzf tbb22_20090809oss_src.tgz
</pre>
<p>Next the customized patches get applied to some of the library sources and each is built to install in the custom directory:</p>
<pre class="brush: bash; title: ; notranslate">
export CMAKE_PREFIX_PATH=`pwd`/local

cd tbb22_20090809oss
gmake
cp -Rp include/tbb/ /usr/include/
cp `pwd`/build/*_release/*.so /usr/lib/
ldconfig
cd ..

cd re2c-0.13.5
./configure --prefix=`pwd`/../local
make install
cd ..

cd libevent-1.4.13-stable
cp ../hiphop-php/src/third_party/libevent.fb-changes.diff .
patch &lt; libevent.fb-changes.diff
./configure --prefix=`pwd`/../local
make install
cd ..

cd curl-7.20.0
cp ../hiphop-php/src/third_party/libcurl.fb-changes.diff .
patch -p0 &lt; libcurl.fb-changes.diff
./configure --prefix=`pwd`/../local
make install
cd ..
</pre>
<p>There is one problem at this point that requires a little surgery on the HipHop PHP source itself. There is more about this in <a href="http://github.com/facebook/hiphop-php/issues#issue/6">issue #6</a> and once it gets fixed this won&#039;t need to be done. </p>
<pre class="brush: bash; title: ; notranslate">
cd hiphop-php
echo &quot;#ifndef LHASH&quot; &gt;&gt; src/cpp/ext/ext_openssl.h
echo &quot;#define LHASH LHASH_OF(CONF_VALUE)&quot; &gt;&gt; src/cpp/ext/ext_openssl.h
echo &quot;#endif&quot; &gt;&gt; src/cpp/ext/ext_openssl.h
</pre>
<p>And at last it is time to compile HipHop PHP itself:</p>
<pre class="brush: bash; title: ; notranslate">
git submodule init
git submodule update
export HPHP_HOME=`pwd`
export HPHP_LIB=`pwd`/bin
cmake .
make
</pre>
<p>It takes about 20 minutes to compile everything. Once the compile is done you are ready to roll. Check out the <a href="http://wiki.github.com/facebook/hiphop-php/running-hiphop">running HipHop wiki page</a> to learn how to run the resulting binary. One important thing to note is that you need to make sure you have the correct environment variables set when you go to compile things. I created a little file I can source with the following in it:</p>
<pre class="brush: bash; title: ; notranslate">
export HPHP_BASE=&lt;path to the first directory&gt;
export CMAKE_PREFIX_PATH=$HPHP_BASE/local
export HPHP_HOME=$HPHP_BASE/hiphop-php
export HPHP_LIB=$HPHP_HOME/bin
</pre>
<p>For those who just want it to go I&#039;ve put all of the above into one script that can be found <a href="http://www.ioncannon.net/examples/hiphopgo.sh">here</a>. If you are going from Fedora 8 to Fedora 12 on an EC2 node you can get a script for that <a href="http://www.ioncannon.net/examples/64bitfedora12ec2.sh">here</a>.</p>
<p>Now if you want to do this on a 32 bit Fedora 12 install you will need to modify the source first. The easiest way I know of doing this is to look at <a href="http://github.com/carsonmcdonald/hiphop-php/commit/792a37cb10514178341877c1425e2f3884898645">this commit log</a> or clone my version that can be found here:</p>
<pre class="brush: bash; title: ; notranslate">
git clone git://github.com/carsonmcdonald/hiphop-php.git
</pre>
<p>Please note that my version my not be up to date and the modifications to get the source to build on the 32 bit system may not be 100% correct. My goal was to get it to build and run on a 32 bit system but I don&#039;t have the time to very much more than that.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ioncannon.net/programming/918/building-hiphop-php-for-fedora-12-on-64-bit-and-32-bit-systems/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>PHP MySQLi and Multiple Prepared Statements</title>
		<link>http://www.ioncannon.net/programming/889/php-mysqli-and-multiple-prepared-statements/</link>
		<comments>http://www.ioncannon.net/programming/889/php-mysqli-and-multiple-prepared-statements/#comments</comments>
		<pubDate>Tue, 09 Feb 2010 11:58:44 +0000</pubDate>
		<dc:creator>carson</dc:creator>
				<category><![CDATA[programming]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[mysqli]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://www.ioncannon.net/?p=889</guid>
		<description><![CDATA[While sprucing up the PHP code I use to provide my own Stack Overflow API for GeeQe I ran into an error caused by trying to use multiple prepared statements with MySQLi. It turned up when I tried to execute one prepared statement while looping over the result set from another prepared statement that were [...]]]></description>
			<content:encoded><![CDATA[<p>While sprucing up the PHP code I use to provide my own Stack Overflow API for <a href="http://www.ioncannon.net/projects/geeqe/">GeeQe</a> I ran into an error caused by trying to use multiple prepared statements with <a href="http://www.php.net/manual/en/book.mysqli.php">MySQLi</a>. It turned up when I tried to execute one prepared statement while looping over the result set from another prepared statement that were both created on the same connection. What came out was the following error:</p>
<p>&#034;Commands out of sync; you can&#039;t run this command now&#034;</p>
<p>Details about this error can be found in the <a href="http://dev.mysql.com/doc/refman/5.0/en/commands-out-of-sync.html">mysql docs</a>. Reading those details makes it clear that the result sets of a prepared statement execution need to be fetched completely before executing another prepared statement on the same connection.</p>
<p><span id="more-889"></span></p>
<p>Fixing the issue can be accomplished by using the <a href="http://php.net/manual/en/mysqli-stmt.store-result.php">store result</a> call. Here is an example of what I initially was trying to do:</p>
<pre class="brush: php; title: ; notranslate">
&lt;?php

  $db_connection = new mysqli('127.0.0.1', 'user', '', 'test');

  $post_stmt = $db_connection-&gt;prepare(&quot;select id, title from post where id = 1000&quot;);
  $comment_stmt = $db_connection-&gt;prepare(&quot;select user_id from comment where post_id = ?&quot;);

  if ($post_stmt-&gt;execute())
  {
    $post_stmt-&gt;bind_result($post_id, $post_title);

    if ($post_stmt-&gt;fetch())
    {
      $comments = array();

      $comment_stmt-&gt;bind_param('i', $post_id);
      if ($comment_stmt-&gt;execute())
      {
        $comment_stmt-&gt;bind_result($user_id);
        while ($comment_stmt-&gt;fetch())
        {
          array_push($comments, array('user_id' =&gt; $user_id));
        }
      }
      else
      {
        printf(&quot;Comment statement error: %s\n&quot;, $comment_stmt-&gt;error);
      }
    }
  }
  else
  {
    printf(&quot;Post statement error: %s\n&quot;, $post_stmt-&gt;error);
  }

  $post_stmt-&gt;close();
  $comment_stmt-&gt;close();

  $db_connection-&gt;close();

  printf(&quot;ID: %d -&gt; %s\n&quot;, $post_id, $post_title);
  print_r($comments);
?&gt;
</pre>
<p>The above will result in the following error:</p>
<div class="codesnip-container" >
<div class="text codesnip" style="font-family:monospace;">Comment statement error: Commands out of sync; you can&#039;t run this command now<br />
PHP Notice: &nbsp;Undefined variable: post_title in error.php on line 41<br />
ID: 9033 -&amp;gt; <br />
Array<br />
(<br />
)</div>
</div>
<p>Here is what needs to be done to make it work correctly:</p>
<pre class="brush: php; title: ; notranslate">
&lt;?php

  $db_connection = new mysqli('127.0.0.1', 'user', '', 'test');

  $post_stmt = $db_connection-&gt;prepare(&quot;select id, title from post where id = 1000&quot;);
  $comment_stmt = $db_connection-&gt;prepare(&quot;select user_id from comment where post_id = ?&quot;);

  if ($post_stmt-&gt;execute())
  {
    $post_stmt-&gt;store_result();
    $post_stmt-&gt;bind_result($post_id, $post_title);

    if ($post_stmt-&gt;fetch())
    {
      $comments = array();

      $comment_stmt-&gt;bind_param('i', $post_id);
      if ($comment_stmt-&gt;execute())
      {
        $comment_stmt-&gt;bind_result($user_id);
        while ($comment_stmt-&gt;fetch())
        {
          array_push($comments, array('user_id' =&gt; $user_id));
        }
      }
      else
      {
        printf(&quot;Comment statement error: %s\n&quot;, $comment_stmt-&gt;error);
      }
    }

    $post_stmt-&gt;free_result();
  }
  else
  {
    printf(&quot;Post statement error: %s\n&quot;, $post_stmt-&gt;error);
  }

  $post_stmt-&gt;close();
  $comment_stmt-&gt;close();

  $db_connection-&gt;close();

  printf(&quot;ID: %d -&gt; %s\n&quot;, $post_id, $post_title);
  print_r($comments);
?&gt;
</pre>
<p>A couple things to note about the above example:</p>
<ul>
<li>The bind and fetch on the statement still works correctly.</li>
<li>Make sure the results are freed when the processing is done.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.ioncannon.net/programming/889/php-mysqli-and-multiple-prepared-statements/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Full Text Search with Sphinx</title>
		<link>http://www.ioncannon.net/programming/685/full-text-search-with-sphinx/</link>
		<comments>http://www.ioncannon.net/programming/685/full-text-search-with-sphinx/#comments</comments>
		<pubDate>Tue, 20 Oct 2009 08:30:28 +0000</pubDate>
		<dc:creator>carson</dc:creator>
				<category><![CDATA[programming]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[sphinx]]></category>

		<guid isPermaLink="false">http://www.ioncannon.net/?p=685</guid>
		<description><![CDATA[While developing my GeeQE iPhone application I decided I needed a way to let users search posts so I started looking around for a simple search engine that I could use with PHP. I took a look at a number of different options like MySQL Full Text search, Sphinx, Solr and others based on Lucene. [...]]]></description>
			<content:encoded><![CDATA[<p>While developing my <a href="http://www.ioncannon.net/projects/geeqe/">GeeQE</a> iPhone application I decided I needed a way to let users search posts so I started looking around for a simple search engine that I could use with PHP. I took a look at a number of different options like <a href="http://dev.mysql.com/doc/refman/5.1/en/fulltext-search.html">MySQL Full Text search</a>, <a href="http://www.sphinxsearch.com/">Sphinx</a>, <a href="http://lucene.apache.org/solr/">Solr</a> and others based on <a href="http://lucene.apache.org/">Lucene</a>. After looking at what it would take to get started with each I decided to go with Sphinx. Sphinx looked like it would be the easiest and quickest to set up, didn&#039;t require a lot of resources to run in an idle state and would integrate with PHP easily.</p>
<p>This post goes over how I went about configuring Sphinx and gives an example of how to integrate it with PHP. I&#039;m using MySQL as the data store filled with the <a href="http://blog.stackoverflow.com/2009/06/stack-overflow-creative-commons-data-dump/">Stack Overflow CC data dump</a> although it should be easy to adapt the instructions to other data sources. To follow along just download a copy of the data dump and use my <a href="http://code.google.com/p/geeqe/source/browse/trunk/scripts/schema.sql">schema</a> and <a href="http://code.google.com/p/geeqe/source/browse/trunk/scripts/load.rb">loader</a> to get the same MySQL database.</p>
<p><span id="more-685"></span></p>
<p>I&#039;ve broken the setup down into the following 4 steps:</p>
<ol>
<li><a href="#sphinx-config">Configuring Sphinx</a></li>
<li><a href="#sphinx-index">Building an Index with Sphinx</a></li>
<li><a href="#sphinx-search">Searching with Sphinx</a></li>
<li><a href="#sphinx-php-api">Using the Sphinx PHP API</a></li>
</ol>
<p>As a side note, Sphinx is available as a package under Fedora 11 and I assume the same of other Linux distributions so it shouldn&#039;t be too hard to install. I am using version 0.9.8.1 compiled from source.</p>
<p><a name="sphinx-config"><b>Configuring Sphinx</b></a></p>
<p>The Sphinx configuration file can be be broken down into different parts with some of those parts being used by the indexer and some by the search service. I have broken the configuration file I am using into different sections but I include the full file for download at the end. The first segment of the configuration describes where to get the data for the index, in this case it is a MySQL database:</p>
<div class="codesnip-container" >
<div class="text codesnip" style="font-family:monospace;">###############################################<br />
## source for the data to be indexed<br />
###############################################</p>
<p>source sosrc<br />
{<br />
&nbsp; &nbsp; &nbsp; &nbsp; type&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = mysql</p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; sql_host&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = localhost<br />
&nbsp; &nbsp; &nbsp; &nbsp; sql_user&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = souser<br />
&nbsp; &nbsp; &nbsp; &nbsp; sql_pass&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =<br />
&nbsp; &nbsp; &nbsp; &nbsp; sql_db&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = so_2009_10<br />
&nbsp; &nbsp; &nbsp; &nbsp; sql_port&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = 3306</p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; sql_query &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = SELECT id, owner_id, UNIX_TIMESTAMP(created) AS date_added, title, body_text FROM post WHERE post_type_id = 1</p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; sql_attr_uint &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = owner_id<br />
&nbsp; &nbsp; &nbsp; &nbsp; sql_attr_timestamp&nbsp; &nbsp; &nbsp; = date_added</p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; sql_ranged_throttle &nbsp; &nbsp; = 0</p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; # document info query, ONLY for CLI search (ie. testing and debugging)<br />
&nbsp; &nbsp; &nbsp; &nbsp; sql_query_info&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = SELECT * FROM post WHERE id=$id<br />
}</div>
</div>
<p><br/></p>
<p>There are a few notable parts to the above. The field &#034;sql_query&#034; defines the query that grabs data from the database for the index. The &#034;sql_attr_*&#034; values point out the columns in the data that will be used in search queries later to sort or group by, more on that in the searching section.</p>
<p>The next section defines the index:</p>
<div class="codesnip-container" >
<div class="text codesnip" style="font-family:monospace;">###############################################<br />
## base search index<br />
###############################################</p>
<p>index so_2009_10<br />
{<br />
&nbsp; &nbsp; &nbsp; &nbsp; source&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = sosrc<br />
&nbsp; &nbsp; &nbsp; &nbsp; path&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = ./data/so_2009_10<br />
&nbsp; &nbsp; &nbsp; &nbsp; docinfo &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = extern<br />
&nbsp; &nbsp; &nbsp; &nbsp; mlock &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = 0</p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; morphology&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = none</p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; min_word_len&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = 1<br />
&nbsp; &nbsp; &nbsp; &nbsp; charset_type&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = sbcs</p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; html_strip&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = 1<br />
}</p>
<p>###############################################<br />
## index that extends the original index<br />
###############################################</p>
<p>index so_2009_10stemmed : so_2009_10<br />
{<br />
&nbsp; &nbsp; &nbsp; &nbsp; path&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = ./data/so_2009_10stemmed<br />
&nbsp; &nbsp; &nbsp; &nbsp; morphology&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = stem_en<br />
}</div>
</div>
<p><br/></p>
<p>In the above there are two index definitions. The first index is very basic and has no morphology defined. The second index is derived from the first index and uses a <a href="http://en.wikipedia.org/wiki/Stemming">stemming</a> morphology. I&#039;m also having any HTML stripped out of the content since there is some in the data and it doesn&#039;t need to be searchable in the index.</p>
<p>The next block in the configuration file defines how the indexer will run. Here I&#039;m limiting the amount of memory used to 32M as an example of what could be done:</p>
<div class="codesnip-container" >
<div class="text codesnip" style="font-family:monospace;">###############################################<br />
## indexer settings<br />
###############################################</p>
<p>indexer<br />
{<br />
&nbsp; &nbsp; &nbsp; &nbsp; mem_limit &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = 32M<br />
}</div>
</div>
<p><br/></p>
<p>The final part of the configuration file sets options for the search daemon:</p>
<div class="codesnip-container" >
<div class="text codesnip" style="font-family:monospace;">###############################################<br />
## searchd settings<br />
###############################################</p>
<p>searchd<br />
{<br />
&nbsp; &nbsp; &nbsp; &nbsp; port&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = 3312<br />
&nbsp; &nbsp; &nbsp; &nbsp; log &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = /tmp/searchd.log<br />
&nbsp; &nbsp; &nbsp; &nbsp; query_log &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = /tmp/query.log</p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; read_timeout&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = 5<br />
&nbsp; &nbsp; &nbsp; &nbsp; max_children&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = 30<br />
&nbsp; &nbsp; &nbsp; &nbsp; pid_file&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = /tmp/searchd.pid<br />
&nbsp; &nbsp; &nbsp; &nbsp; max_matches &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = 100<br />
&nbsp; &nbsp; &nbsp; &nbsp; seamless_rotate &nbsp; &nbsp; &nbsp; &nbsp; = 1<br />
&nbsp; &nbsp; &nbsp; &nbsp; preopen_indexes &nbsp; &nbsp; &nbsp; &nbsp; = 0<br />
&nbsp; &nbsp; &nbsp; &nbsp; unlink_old&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = 1<br />
}</div>
</div>
<p><br/></p>
<p>You can download the full configuration file here: <a href="http://www.ioncannon.net/examples/sphinx.conf">sphinx.conf</a></p>
<p>The above configuration gets you the basics. If you want to find out more you can check out the <a href="http://www.sphinxsearch.com/docs/current.html#indexing">indexing</a> documentation.</p>
<p><a name="sphinx-index"><b>Building an Index with Sphinx</b></a></p>
<p>The complexity of building and maintaining an index goes up with the frequency of the updates that are required to keep it up to date. Luckily the data set I&#039;m working with gets updated once a month so it is fairly static. Because the data is updated monthly I only have to update the index once a month so this makes the indexing simple.</p>
<p>This is the command I use to create the index:</p>
<pre>
<div class="codesnip-container" >
<div class="bash codesnip" style="font-family:monospace;">indexer <span class="re5">--all</span> <span class="re5">--config</span> sphinx.conf</div>
</div>
</pre>
<p>Indexing the entire data set I have only takes a few minutes so re-indexing speed isn&#039;t an issue for me and the resulting index is in the 250M range as of this post so size isn&#039;t necessarily an issue either. However I currently create the index on one machine and then upload it to the server so a 250M transfer could become a bandwidth hog if I needed to update the index more often. If I were able to get deltas from the data dump I would look into using the <a href="http://www.sphinxsearch.com/docs/current.html#index-merging">index merging</a> feature and then transferring the delta index.</p>
<p><a name="sphinx-search"><b>Searching with Sphinx</b></a></p>
<p>Sphinx offers a lot of flexibility in configuring how a search is run and I found myself needing that flexibility to get better results. The search command line interface (see the <a href="http://www.sphinxsearch.com/docs/current.html#ref-search">reference</a> for all the options) can be used to test a few of the options but you can&#039;t do everything with it that you can with the various APIs available.</p>
<p>The first place I started changing defaults with the search is in how it matches the search words. The default is to find all words:</p>
<pre>
<div class="codesnip-container" >
<div class="bash codesnip" style="font-family:monospace;">search <span class="re5">--config</span> sphinx.conf <span class="st0">&quot;php full text search&quot;</span></div>
</div>
</pre>
<p>Here is an example of the output you get from the CLI search:</p>
<div class="codesnip-container" >
<div class="text codesnip" style="font-family:monospace;">Copyright (c) 2001-2008, Andrew Aksyonoff</p>
<p>using config file &#039;sphinx.conf&#039;&#8230;<br />
index &#039;so_2009_10&#039;: query &#039;php full text search &#039;: returned 19 matches of 19 total in 0.061 sec</p>
<p>displaying matches:<br />
1. document=553055, weight=6, owner_id=2287, date_added=Mon Feb 16 11:45:01 2009<br />
&nbsp; &nbsp; &nbsp; &nbsp; id=553055<br />
&nbsp; &nbsp; &nbsp; &nbsp; post_type_id=1<br />
&nbsp; &nbsp; &nbsp; &nbsp; accepted_answer_id=553269<br />
&nbsp; &nbsp; &nbsp; &nbsp; parent_id=(null)<br />
&nbsp; &nbsp; &nbsp; &nbsp; score=6<br />
&nbsp; &nbsp; &nbsp; &nbsp; view_count=604<br />
&nbsp; &nbsp; &nbsp; &nbsp; body_text=truncated&#8230; body text would be here<br />
&nbsp; &nbsp; &nbsp; &nbsp; owner_id=2287<br />
&nbsp; &nbsp; &nbsp; &nbsp; last_editor_user_id=2287<br />
&nbsp; &nbsp; &nbsp; &nbsp; last_editor_display_name=PConroy<br />
&nbsp; &nbsp; &nbsp; &nbsp; last_edit_date=2009-02-18 10:54:44<br />
&nbsp; &nbsp; &nbsp; &nbsp; last_activity_date=2009-03-25 19:42:34<br />
&nbsp; &nbsp; &nbsp; &nbsp; title=Best full text search for mysql?<br />
&nbsp; &nbsp; &nbsp; &nbsp; answer_count=5<br />
&nbsp; &nbsp; &nbsp; &nbsp; comment_count=0<br />
&nbsp; &nbsp; &nbsp; &nbsp; favorite_count=7<br />
&nbsp; &nbsp; &nbsp; &nbsp; created=2009-02-16 11:45:01<br />
&#8230;</div>
</div>
<p><br/></p>
<p>The output shows you the &#034;document id&#034; as well as the weight and any &#034;sql_attr_&#034; values. I used the CLI search to get the weights for each search change I made before I started working with the API. Please note that the extended information in the above example output only shows up if the sql_query_info query is set correctly in the configuration file.</p>
<p>Next I tested with &#034;any&#034; and &#034;extended search version 2&#034; before settling on extended version 2:</p>
<pre>
<div class="codesnip-container" >
<div class="bash codesnip" style="font-family:monospace;">search <span class="re5">--any</span> <span class="re5">--config</span> sphinx.conf <span class="st0">&quot;php full text search&quot;</span>

search <span class="re5">--ext2</span> <span class="re5">--config</span> sphinx.conf <span class="st0">&quot;php full text search&quot;</span></div>
</div>
</pre>
<p>The CLI search will also perform sorts. It is important to note that you can only sort on values that were indexed using the &#034;sql_attr_&#034;* configuration options. Here is an example of sorting by an indexed value:</p>
<pre>
<div class="codesnip-container" >
<div class="bash codesnip" style="font-family:monospace;">search <span class="re5">--sortby</span> <span class="st0">&quot;date_added desc&quot;</span> <span class="re5">--config</span> sphinx.conf <span class="st0">&quot;php regex&quot;</span></div>
</div>
</pre>
<p>This is what happens when you try to sort based on an un-index attribute:</p>
<pre>
<div class="codesnip-container" >
<div class="bash codesnip" style="font-family:monospace;">search <span class="re5">--sortby</span> <span class="st0">&quot;id desc&quot;</span> <span class="re5">--config</span> sphinx.conf <span class="st0">&quot;php regex&quot;</span>

Sphinx 0.9.8.1-release <span class="br0">&#40;</span>r1533<span class="br0">&#41;</span>
Copyright <span class="br0">&#40;</span>c<span class="br0">&#41;</span> 2001-2008, Andrew Aksyonoff

using config <span class="kw2">file</span> <span class="st_h">'sphinx.conf'</span>...
index <span class="st_h">'so_2009_10'</span>: search error: failed to create sorting queue: sort-by attribute <span class="st_h">'id'</span> not found.</div>
</div>
</pre>
<p>One of the things that the CLI search interface can&#039;t do is change <a href="http://www.sphinxsearch.com/docs/current.html#weighting">weightings</a> for the different data points. I found that weighting the title more than the body made sense and that is reflected in the PHP code that follows in the API section.  </p>
<p>The last part of searching to cover is searchd server. This is the integration point for the various Sphinx APIs and I assume it is running for the examples in the PHP API section. If the configuration example given in the first section is used all that needs to be done is to start the daemon. The following is a simple init script I use to start and stop searchd:</p>
<pre>
<div class="codesnip-container" >
<div class="bash codesnip" style="font-family:monospace;"><span class="co0">#!/bin/sh</span>

<span class="kw1">case</span> <span class="st0">&quot;$1&quot;</span> <span class="kw1">in</span>
<span class="st_h">'start'</span><span class="br0">&#41;</span>
searchd <span class="re5">--config</span> <span class="sy0">/</span>etc<span class="sy0">/</span>sphinx<span class="sy0">/</span>sphinx.conf
<span class="sy0">;;</span>
<span class="st_h">'stop'</span><span class="br0">&#41;</span>
<span class="kw2">killall</span> <span class="re5">-9</span> searchd;
<span class="sy0">;;</span>
<span class="sy0">*</span><span class="br0">&#41;</span>
<span class="kw3">echo</span> <span class="st0">&quot;Usage: $0 { start | stop}&quot;</span>
<span class="kw3">exit</span> 1
<span class="sy0">;;</span>
<span class="kw1">esac</span>
<span class="kw3">exit</span> <span class="nu0">0</span>
<span class="co0">#</span></div>
</div>
</pre>
<p><a name="sphinx-php-api"><b>Using the Sphinx PHP API</b></a></p>
<p>The Sphinx PHP API is included in the <a href="http://www.sphinxsearch.com/downloads.html">Sphinx source</a>. The API is contained in one file named sphinxapi.php that is located in the api directory of the source. Make sure the library is in a place where it can be included before trying out the examples. There is also some <a href="http://www.sphinxsearch.com/wiki/doku.php?id=php_api_docs">documentation</a> for the PHP API.</p>
<p>In the following example I&#039;m running the search query then serializing it to JSON format. Notice that the title is given a weight of 70 and the body_text is given a weight of 30 so the results will focus more on the title than what is in the body:</p>
<div class="codesnip-container" >
<div class="php codesnip" style="font-family:monospace;"><span class="kw2">&lt;?php</span></p>
<p><span class="kw1">require_once</span><span class="br0">&#40;</span><span class="st_h">&#039;sphinxapi.php&#039;</span><span class="br0">&#41;</span><span class="sy0">;</span></p>
<p><span class="co1">// Create the client, tell it where the server </span><br />
<span class="co1">// is and how long to wait for a response.</span><br />
<span class="re0">$sphinxClient</span> <span class="sy0">=</span> <span class="kw2">new</span> SphinxClient<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
<span class="re0">$sphinxClient</span><span class="sy0">-&gt;</span><span class="me1">SetServer</span><span class="br0">&#40;</span> <span class="st_h">&#039;localhost&#039;</span><span class="sy0">,</span> 3312 <span class="br0">&#41;</span><span class="sy0">;</span><br />
<span class="re0">$sphinxClient</span><span class="sy0">-&gt;</span><span class="me1">SetConnectTimeout</span><span class="br0">&#40;</span> 1 <span class="br0">&#41;</span><span class="sy0">;</span></p>
<p><span class="co1">// This gives the title more weight than the </span><br />
<span class="co1">// body text for searches.</span><br />
<span class="re0">$sphinxClient</span><span class="sy0">-&gt;</span><span class="me1">SetFieldWeights</span><span class="br0">&#40;</span><a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span><span class="st_h">&#039;title&#039;</span> <span class="sy0">=&gt;</span> <span class="nu0">70</span><span class="sy0">,</span> <span class="st_h">&#039;body_text&#039;</span> <span class="sy0">=&gt;</span> 30<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span></p>
<p><span class="co1">// Use the exteneded v2 match type</span><br />
<span class="re0">$sphinxClient</span><span class="sy0">-&gt;</span><span class="me1">SetMatchMode</span><span class="br0">&#40;</span> SPH_MATCH_EXTENDED2 <span class="br0">&#41;</span><span class="sy0">;</span></p>
<p><span class="co1">// Set the maximum number of search results to return</span><br />
<span class="re0">$sphinxClient</span><span class="sy0">-&gt;</span><span class="me1">SetLimits</span><span class="br0">&#40;</span> 0<span class="sy0">,</span> 20<span class="sy0">,</span> 1000 <span class="br0">&#41;</span><span class="sy0">;</span></p>
<p><span class="co1">// Set how to rank the weighted values</span><br />
<span class="re0">$sphinxClient</span><span class="sy0">-&gt;</span><span class="me1">SetRankingMode</span><span class="br0">&#40;</span> SPH_RANK_PROXIMITY_BM25 <span class="br0">&#41;</span><span class="sy0">;</span></p>
<p><span class="co1">// Give me back the results as an array</span><br />
<span class="re0">$sphinxClient</span><span class="sy0">-&gt;</span><span class="me1">SetArrayResult</span><span class="br0">&#40;</span> <span class="kw4">true</span> <span class="br0">&#41;</span><span class="sy0">;</span></p>
<p><span class="re0">$searchQuery</span> <span class="sy0">=</span> <span class="re0">$_GET</span><span class="br0">&#91;</span><span class="st_h">&#039;query&#039;</span><span class="br0">&#93;</span><span class="sy0">;</span><br />
<span class="re0">$searchResults</span> <span class="sy0">=</span> <span class="re0">$sphinxClient</span><span class="sy0">-&gt;</span><span class="me1">Query</span><span class="br0">&#40;</span> <span class="re0">$searchQuery</span><span class="sy0">,</span> <span class="st_h">&#039;*&#039;</span> <span class="br0">&#41;</span><span class="sy0">;</span></p>
<p><span class="re0">$jhash</span> <span class="sy0">=</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></p>
<p><span class="kw1">if</span> <span class="br0">&#40;</span> <span class="re0">$searchResults</span> <span class="sy0">===</span> <span class="kw4">false</span> <span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; <span class="re0">$jhash</span><span class="br0">&#91;</span><span class="st_h">&#039;status&#039;</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="st_h">&#039;failed&#039;</span><span class="sy0">;</span><br />
&nbsp; <span class="re0">$jhash</span><span class="br0">&#91;</span><span class="st_h">&#039;status_message&#039;</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="re0">$sphinxClient</span><span class="sy0">-&gt;</span><span class="me1">GetLastError</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
<span class="br0">&#125;</span><br />
<span class="kw1">else</span><br />
<span class="br0">&#123;</span><br />
&nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span> <span class="re0">$sphinxClient</span><span class="sy0">-&gt;</span><span class="me1">GetLastWarning</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="br0">&#41;</span><br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="re0">$jhash</span><span class="br0">&#91;</span><span class="st_h">&#039;status&#039;</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="st_h">&#039;warning&#039;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="re0">$jhash</span><span class="br0">&#91;</span><span class="st_h">&#039;status_message&#039;</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="re0">$sphinxClient</span><span class="sy0">-&gt;</span><span class="me1">GetLastWarning</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; <span class="br0">&#125;</span><br />
&nbsp; <span class="kw1">else</span><br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="re0">$jhash</span><span class="br0">&#91;</span><span class="st_h">&#039;status&#039;</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="st_h">&#039;good&#039;</span><span class="sy0">;</span><br />
&nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; <span class="re0">$jhash</span><span class="br0">&#91;</span><span class="st_h">&#039;result_total&#039;</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="re0">$searchResults</span><span class="br0">&#91;</span><span class="st_h">&#039;total&#039;</span><span class="br0">&#93;</span><span class="sy0">;</span><br />
&nbsp; <span class="re0">$jhash</span><span class="br0">&#91;</span><span class="st_h">&#039;result_found&#039;</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="re0">$searchResults</span><span class="br0">&#91;</span><span class="st_h">&#039;total_found&#039;</span><span class="br0">&#93;</span><span class="sy0">;</span></p>
<p>&nbsp; <span class="re0">$jhash_matches</span> <span class="sy0">=</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span> <a href="http://www.php.net/is_array"><span class="kw3">is_array</span></a><span class="br0">&#40;</span><span class="re0">$searchResults</span><span class="br0">&#91;</span><span class="st0">&quot;matches&quot;</span><span class="br0">&#93;</span><span class="br0">&#41;</span> <span class="br0">&#41;</span><br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="re0">$row_ids</span> <span class="sy0">=</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">foreach</span> <span class="br0">&#40;</span> <span class="re0">$searchResults</span><span class="br0">&#91;</span><span class="st0">&quot;matches&quot;</span><span class="br0">&#93;</span> <span class="kw1">as</span> <span class="re0">$docinfo</span> <span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; <a href="http://www.php.net/array_push"><span class="kw3">array_push</span></a><span class="br0">&#40;</span><span class="re0">$row_ids</span><span class="sy0">,</span> <a href="http://www.php.net/mysql_real_escape_string"><span class="kw3">mysql_real_escape_string</span></a><span class="br0">&#40;</span><span class="re0">$docinfo</span><span class="br0">&#91;</span><span class="st_h">&#039;id&#039;</span><span class="br0">&#93;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; <span class="re0">$jhash</span><span class="br0">&#91;</span><span class="st_h">&#039;matches&#039;</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="re0">$jhash_matches</span><span class="sy0">;</span><br />
<span class="br0">&#125;</span></p>
<p><span class="kw1">echo</span> <a href="http://www.php.net/json_encode"><span class="kw3">json_encode</span></a><span class="br0">&#40;</span><span class="re0">$jhash</span><span class="br0">&#41;</span><span class="sy0">;</span></p>
<p><span class="sy1">?&gt;</span></div>
</div>
<p>Although I picked PHP there are a wide range of language specific libraries available.</p>
<p>Overall it didn&#039;t seem too difficult to set up Sphinx and have it serving search results quickly. I liked that it is very light weight and doesn&#039;t need a lot of resources or require a lot of extra parts to be installed.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ioncannon.net/programming/685/full-text-search-with-sphinx/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Anonymous functions in PHP</title>
		<link>http://www.ioncannon.net/programming/126/anonymous-functions-in-php/</link>
		<comments>http://www.ioncannon.net/programming/126/anonymous-functions-in-php/#comments</comments>
		<pubDate>Fri, 18 May 2007 14:17:26 +0000</pubDate>
		<dc:creator>carson</dc:creator>
				<category><![CDATA[programming]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://www.ioncannon.net/uncategorized/126/anonymous-functions-in-php/</guid>
		<description><![CDATA[I ran into this and found it interesting. Someone has added support for anonymous functions in PHP. With the patch you can now do stuff like: Before you had to use a funky function generation call. Tags: php]]></description>
			<content:encoded><![CDATA[<p>I ran into this and found it interesting. Someone has added support for <a href="http://devzone.zend.com/node/view/id/2013">anonymous functions in PHP</a>.</p>
<p>With the patch you can now do stuff like:</p>
<div class="codesnip-container" >
<div class="php codesnip" style="font-family:monospace;"><span class="re0">$data</span> <span class="sy0">=</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span><span class="st0">&quot;zoo&quot;</span><span class="sy0">,</span> <span class="st0">&quot;orange&quot;</span><span class="sy0">,</span> <span class="st0">&quot;car&quot;</span><span class="sy0">,</span> <span class="st0">&quot;lemon&quot;</span><span class="sy0">,</span> <span class="st0">&quot;apple&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
<a href="http://www.php.net/usort"><span class="kw3">usort</span></a><span class="br0">&#40;</span><span class="re0">$data</span><span class="sy0">,</span> <span class="kw2">function</span><span class="br0">&#40;</span><span class="re0">$a</span><span class="sy0">,</span> <span class="re0">$b</span><span class="br0">&#41;</span> <span class="br0">&#123;</span> <span class="kw1">return</span> <a href="http://www.php.net/strcmp"><span class="kw3">strcmp</span></a><span class="br0">&#40;</span><span class="re0">$a</span><span class="sy0">,</span> <span class="re0">$b</span><span class="br0">&#41;</span><span class="sy0">;</span> <span class="br0">&#125;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
<a href="http://www.php.net/var_dump"><span class="kw3">var_dump</span></a><span class="br0">&#40;</span><span class="re0">$data</span><span class="br0">&#41;</span><span class="sy0">;</span> <span class="co2"># data is sorted alphabetically</span></div>
</div>
<p>Before you had to use a funky function generation call.</p>
<p>Tags: <a href="http://technorati.com/tag/php" rel="tag">php</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.ioncannon.net/programming/126/anonymous-functions-in-php/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Upgrade to PHP 5.2 and Get JSON For Free</title>
		<link>http://www.ioncannon.net/programming/103/upgrade-to-php-52-and-get-json-for-free/</link>
		<comments>http://www.ioncannon.net/programming/103/upgrade-to-php-52-and-get-json-for-free/#comments</comments>
		<pubDate>Tue, 14 Nov 2006 16:19:20 +0000</pubDate>
		<dc:creator>carson</dc:creator>
				<category><![CDATA[programming]]></category>
		<category><![CDATA[ajax]]></category>
		<category><![CDATA[json]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://www.ioncannon.net/php/103/upgrade-to-php-52-and-get-json-for-free/</guid>
		<description><![CDATA[A few days ago when PHP 5.2 was released one of the things that caught my eye was that it now includes the JSON extension. For anyone doing AJAXy type stuff JSON is an easy way to martial your data between your server side language and javascript. For the longest time I&#039;ve been using the [...]]]></description>
			<content:encoded><![CDATA[<p>A few days ago when <a href="http://www.php.net/releases/5_2_0.php">PHP 5.2 was released</a> one of the things that caught my eye was that it now includes the JSON extension. For anyone doing AJAXy type stuff <a href="http://json.org/">JSON</a> is an easy way to martial your data between your server side language and javascript.</p>
<p>For the longest time I&#039;ve been using the <a href="http://mike.teczno.com/json.html">older PHP JSON library</a> to do JSON with PHP but now that the extension is included in the core I decided it was time to test it out. I took a couple of minutes and converted my <a href="http://www.ioncannon.net/dnsbl/">DNSBL checker</a> as a test since it has a fairly large data-set that gets converted and sent back. The <a href="http://us2.php.net/json">json functions</a> provided by the extension are probably easier to use since the JSON library needed you to create an object first but that wasn&#039;t a real issue. After making the change I could tell JSON extension was faster than the library. After a little digging I found that someone has done a little <a href="http://www.aurore.net/projects/php-json/">extension vs library</a> testing and claims the JSON extension is 153 times as fast as the library.</p>
<p>Tags: <a href="http://technorati.com/tag/php" rel="tag">php</a>, <a href="http://technorati.com/tag/ajax" rel="tag"> ajax</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.ioncannon.net/programming/103/upgrade-to-php-52-and-get-json-for-free/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Creating S3 URLs that expire using PHP</title>
		<link>http://www.ioncannon.net/programming/21/creating-s3-urls-that-expire-using-php/</link>
		<comments>http://www.ioncannon.net/programming/21/creating-s3-urls-that-expire-using-php/#comments</comments>
		<pubDate>Thu, 01 Jun 2006 16:50:24 +0000</pubDate>
		<dc:creator>carson</dc:creator>
				<category><![CDATA[programming]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[s3]]></category>

		<guid isPermaLink="false">http://www.ioncannon.net/uncategorized/21/creating-s3-urls-that-expire-using-php/</guid>
		<description><![CDATA[After reading this post on the S3 forum I realized that other people are thinking about doing some of the same stuff I have. paolonew was looking for a way to for a way to create URLs to S3 objects that expired. I did this a while back when I was thinking about how to [...]]]></description>
			<content:encoded><![CDATA[<p>After reading <a href="http://developer.amazonwebservices.com/connect/thread.jspa?threadID=10726&#38;tstart=0">this post on the S3 forum</a> I realized that other people are thinking about doing some of the same stuff I have. paolonew was looking for a way to for a way to create URLs to S3 objects that expired. I did this a while back when I was thinking about how to host objects that I wanted to protect with some outside scheme. The confusion on the forum seemed to be about the timestamps used to expire the URL. For PHP it is fairly easy.</p>
<p><span id="more-21"></span></p>
<p>To clear up the expiration time issue I think these two steps are needed:</p>
<p>1) Keep in mind that the HTTP header dates <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html">must</a> be in GMT.<br />
2) The PHP function <a href="http://us3.php.net/manual/en/function.time.php">time()</a> returns the seconds since the epoch January 1 1970 00:00:00 GMT). Notice here this is in GMT as well.<br />
3) The HTTP Date header you see in a response from an S3 server is the time on that server. The machine you use to sign your request should be synced with that time. I think a good guess is that all the Amazon servers are synced with the atomic clock.</p>
<p>There isn&#039;t much to securing a URL after you have that tucked away. Here is an example that will sign a URL so that it is valid for 60 seconds:</p>
<div class="codesnip-container" >
<div class="php codesnip" style="font-family:monospace;"><span class="kw2">&lt;?php</span></p>
<p><span class="kw1">require_once</span><span class="br0">&#40;</span><span class="st_h">&#039;Crypt/HMAC.php&#039;</span><span class="br0">&#41;</span><span class="sy0">;</span></p>
<p><span class="kw1">echo</span> getS3Redirect<span class="br0">&#40;</span><span class="st0">&quot;/test.jpg&quot;</span><span class="br0">&#41;</span> <span class="sy0">.</span> <span class="st0">&quot;<span class="es1">\n</span>&quot;</span><span class="sy0">;</span></p>
<p><span class="kw2">function</span> getS3Redirect<span class="br0">&#40;</span><span class="re0">$objectName</span><span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; <span class="re0">$S3_URL</span> <span class="sy0">=</span> <span class="st0">&quot;http://s3.amazonaws.com&quot;</span><span class="sy0">;</span><br />
&nbsp; <span class="re0">$keyId</span> <span class="sy0">=</span> <span class="st0">&quot;your key&quot;</span><span class="sy0">;</span><br />
&nbsp; <span class="re0">$secretKey</span> <span class="sy0">=</span> <span class="st0">&quot;your secret&quot;</span><span class="sy0">;</span><br />
&nbsp; <span class="re0">$expires</span> <span class="sy0">=</span> <a href="http://www.php.net/time"><span class="kw3">time</span></a><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="sy0">+</span> <span class="nu0">60</span><span class="sy0">;</span><br />
&nbsp; <span class="re0">$bucketName</span> <span class="sy0">=</span> <span class="st0">&quot;/your bucket&quot;</span><span class="sy0">;</span></p>
<p>&nbsp; <span class="re0">$stringToSign</span> <span class="sy0">=</span> <span class="st0">&quot;GET<span class="es1">\n</span><span class="es1">\n</span><span class="es1">\n</span><span class="es4">$expires</span><span class="es1">\n</span><span class="es4">$bucketName</span><span class="es4">$objectName</span>&quot;</span><span class="sy0">;</span><br />
&nbsp; <span class="re0">$hasher</span> <span class="sy0">=&amp;</span>amp<span class="sy0">;</span> <span class="kw2">new</span> Crypt_HMAC<span class="br0">&#40;</span><span class="re0">$secretKey</span><span class="sy0">,</span> <span class="st0">&quot;sha1&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; <span class="re0">$sig</span> <span class="sy0">=</span> <a href="http://www.php.net/urlencode"><span class="kw3">urlencode</span></a><span class="br0">&#40;</span>hex2b64<span class="br0">&#40;</span><span class="re0">$hasher</span><span class="sy0">-&amp;</span>gt<span class="sy0">;</span>hash<span class="br0">&#40;</span><span class="re0">$stringToSign</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span></p>
<p>&nbsp; <span class="kw1">return</span> <span class="st0">&quot;<span class="es4">$S3_URL</span><span class="es4">$bucketName</span><span class="es4">$objectName</span>?AWSAccessKeyId=<span class="es4">$keyId</span>&amp;amp;Expires=<span class="es4">$expires</span>&amp;amp;Signature=<span class="es4">$sig</span>&quot;</span><span class="sy0">;</span><br />
<span class="br0">&#125;</span></p>
<p><span class="kw2">function</span> hex2b64<span class="br0">&#40;</span><span class="re0">$str</span><span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="re0">$raw</span> <span class="sy0">=</span> <span class="st_h">&#034;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">for</span> <span class="br0">&#40;</span><span class="re0">$i</span><span class="sy0">=</span><span class="nu0">0</span><span class="sy0">;</span> <span class="re0">$i</span> <span class="sy0">&amp;</span>lt<span class="sy0">;</span> <a href="http://www.php.net/strlen"><span class="kw3">strlen</span></a><span class="br0">&#40;</span><span class="re0">$str</span><span class="br0">&#41;</span><span class="sy0">;</span> <span class="re0">$i</span><span class="sy0">+=</span>2<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$raw</span> <span class="sy0">.=</span> <a href="http://www.php.net/chr"><span class="kw3">chr</span></a><span class="br0">&#40;</span><a href="http://www.php.net/hexdec"><span class="kw3">hexdec</span></a><span class="br0">&#40;</span><a href="http://www.php.net/substr"><span class="kw3">substr</span></a><span class="br0">&#40;</span><span class="re0">$str</span><span class="sy0">,</span> <span class="re0">$i</span><span class="sy0">,</span> 2<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="kw1">return</span> <a href="http://www.php.net/base64_encode"><span class="kw3">base64_encode</span></a><span class="br0">&#40;</span><span class="re0">$raw</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
<span class="br0">&#125;</span></p>
<p><span class="sy1">?&gt;</span></div>
</div>
<p>The hex2b64 function was pulled from the amazon S3 PHP example library.
</p>
<p>Tags: <a href="http://technorati.com/tag/amazon+s3" rel="tag">amazon s3</a>, <a href="http://technorati.com/tag/s3" rel="tag"> s3</a>, <a href="http://technorati.com/tag/php" rel="tag"> php</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.ioncannon.net/programming/21/creating-s3-urls-that-expire-using-php/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>

