<?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>Coding My Thoughts &#187; PHP</title>
	<atom:link href="http://marianoiglesias.com.ar/category/php/feed/" rel="self" type="application/rss+xml" />
	<link>http://marianoiglesias.com.ar</link>
	<description>A glimpse at a coder&#039;s troubled mind</description>
	<lastBuildDate>Tue, 03 Jan 2012 16:16:38 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Route middleware with Lithium</title>
		<link>http://marianoiglesias.com.ar/php/route-middleware-with-lithium/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=route-middleware-with-lithium</link>
		<comments>http://marianoiglesias.com.ar/php/route-middleware-with-lithium/#comments</comments>
		<pubDate>Tue, 03 Jan 2012 16:03:51 +0000</pubDate>
		<dc:creator>mariano</dc:creator>
				<category><![CDATA[Lithium]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://marianoiglesias.com.ar/?p=232</guid>
		<description><![CDATA[I spend most of my day switching between languages. Sometimes I start the morning with an early dose of LUA, then I get a lot of Python and C++, followed by the yummy dessert that is Node.js. But there&#8217;s always room for PHP. So let&#8217;s talk about PHP today. People often think that when you [...]
No related posts.]]></description>
			<content:encoded><![CDATA[<p>I spend most of my day switching between languages. Sometimes I start the morning with an early dose of LUA, then I get a lot of Python and C++, followed by the yummy dessert that is Node.js. But there&#8217;s always room for PHP. So let&#8217;s talk about PHP today.</p>
<p>People often think that when you are coding PHP you have to do things &#8220;the PHP way&#8221;. Well, let me clear it up for you: there&#8217;s no such thing as &#8220;the PHP way&#8221;. If there&#8217;s something that defines PHP is its flexibility to be as ugly or as beautiful as you want it to be. With that in mind, what prevents you from taking the lessons learned in one language to another?</p>
<p>That&#8217;s the premise I always have when I&#8217;m coding. Let me give you a for instance. On Node.js, I normally use <a href="http://expressjs.com/" rel="nofollow" >express.js</a> as a framework. Check it out, it&#8217;s pretty awesome. One of the things I love in express.js, is its <a href="http://expressjs.com/guide.html#route-middleware" rel="nofollow" >route middleware</a> capabilities. When I code in any PHP framework, that&#8217;s one of the things I miss the most.</p>
<p>Not many PHP frameworks are flexible enough to support such concepts. One of them is, though. <a href="http://lithify.me" rel="nofollow" >Lithium</a> has many of the things I love about PHP (5.3+ obviously, as I consider anything &lt; 5.3 a waste of my time lately), and some of the things I love about other languages. One of them is Lithium&#8217;s addiction to closures. I love it. They allowed me to take the concept of express.js&#8217; route middleware and apply it to my PHP code.</p>
<p>Let&#8217;s start with a dummy application skeleton. A users table:</p>
<pre class="brush:sql">
CREATE TABLE `users`(
    `id` INT NOT NULL AUTO_INCREMENT,
    `email` VARCHAR(255) NOT NULL,
    `password` VARCHAR(255) NOT NULL,
    PRIMARY KEY(`id`)
);

INSERT INTO `users`(`email`, `password`) VALUES(
    'test@email.com', '$2a$04$U7qYPVYq2YBxqfHL8F2pteERxQYwLTVtAjMIh48Lef9sLSiMVtGHy',
    'john@email.com', '$2a$04$U7qYPVYq2YBxqfHL8F2pteERxQYwLTVtAjMIh48Lef9sLSiMVtGHy'
);
</pre>
<p>In your <code>app/config/bootstrap/connections.php</code>, make sure you uncomment the default database and hook it up to the database owning the table we just created. We&#8217;ll also be using sessions, so uncomment the <code>session.php</code> reference in <code>app/config/bootstrap.php</code>. You may have noticed that our initial users have a password set to a specific value, which means a specific salt was used (the password hashed there in plain text is &#8216;password&#8217;, minus the quotes.). So go ahead and add the following to your <code>app/config/bootstrap/session.php</code> file:</p>
<pre class="brush:php">
use lithium\security\Auth;
use lithium\security\Password;

$salt = '$2a$04$U7qYPVYq2YBxqfHL8F2pte';
Auth::config(array(¬
    'adapter' =&gt; 'Form',
    'model' =&gt; 'Users',
    'filters' =&gt; array('password' => function($text) use($salt){
        return Password::hash($text, $salt);
    }),
    'validators' =&gt; array(
        'password' =&gt; function($form, $data) {
            return (strcmp($form, $data) === 0);
        }
    ),
    'fields' =&gt; array('email', 'password')
));
</pre>
<p>The salt was generated with a call to <code>\lithium\security\Password::salt('bf', 4)</code>. I always use blowfish for password hashing (and 2^16 iterations in production). If you don&#8217;t use blowfish, <a href="http://codahale.com/how-to-safely-store-a-password/" rel="nofollow" >here&#8217;s why you should</a>. Anyway so you may want to store the hash on a better, configurable approach. I opted for a simple variable for this example. Once the salt is defined, I went ahead and configured <code>Auth</code> to use lithium&#8217;s <code>Password::hash()</code> method for hashing using the generated salt, and telling it how to compare hashed passwords against the database value. Pretty simple.</p>
<p>Let&#8217;s now build the <code>Users</code> model. It won&#8217;t have anything in there, really. So just create your <code>app/models/User.php</code> file with the following contents:</p>
<pre class="brush:php">
&lt;?php
namespace app\models;

class Users extends \lithium\data\Model {
}
?&gt;
</pre>
<p>Now the controller. Create a file named <code>app/controllers/UsersController.php</code> with the following contents:</p>
<pre class="brush:php">
&lt;?php
namespace app\controllers;

use lithium\security\Auth;

class UsersController extends \lithium\action\Controller {
    public function login() {
        if (!empty($this-&gt;request-&gt;data)) {
            $user = Auth::check('default', $this-&gt;request);
            if ($user) {
                $this-&gt;redirect(array('action' =&gt; 'view', 'id' =&gt; $user['id']), array('exit' =&gt; true));
            }
        }
    }

    public function logout() {
        Auth::clear('default');
    }
}
?&gt;
</pre>
<p>Nothing really complicated there. Don&#8217;t forget the view in <code>app/views/users/login.html.php</code>:</p>
<pre class="brush:php">
&lt;?php
echo $this-&gt;form-&gt;create();
echo $this-&gt;form-&gt;field('email');
echo $this-&gt;form-&gt;field('password', array('type' =&gt; 'password'));
echo $this-&gt;form-&gt;submit('Login');
echo $this-&gt;form-&gt;end();
?&gt;
</pre>
<p>That should give you a working login / logout. Add some dummy actions to the <code>UsersController.php</code> file:</p>
<pre class="brush:php">
public function view() {
    echo 'view';
    $this-&gt;_stop();
}

public function edit() {
    echo 'edit';
    $this-&gt;_stop();
}
</pre>
<p>Ok now we are ready to play with some route middleware. What we want to achieve is the following:</p>
<ul>
<li>No action named <code>edit</code>, on <strong>any controller</strong>, should be accessible without a logged in user.</li>
<li>When accessing either the <code>Users::edit</code> or <code>Users::view</code> action, there should be an ID specified as a route parameter, and it should match an existing <code>User</code> record.</li>
<li>When accessing the <code>Users::edit</code> action, the given user should match the currently logged in user.</li>
</ul>
<p>These are pretty basic security checks that you would normally put on the controller. Not this time. Edit your <code>app/config/routes.php</code> file and add the following right below the <code>use</code> statements found at the beginning of the file:</p>
<pre class="brush:php">
use lithium\net\http\RoutingException;
use lithium\action\Response;
use lithium\security\Auth;
use app\models\Users;
</pre>
<p>These are all classes that we will use in our route middleware. Let&#8217;s start with the first checkpoint we want to achieve: &#8220;<i>No action named <code>edit</code>, on <strong>any controller</strong>, should be accessible without a logged in user</i>&#8220;. Add the following to the <code>routes.php</code> file, below the content we just added:</p>
<pre class="brush:php">
Router::connect('/{:controller}/{:action:edit}/?.*', array(), array(
    'continue' =&gt; true,
    'handler' =&gt; function($request) {
        if (!Auth::check('default')) {
            return new Response(array('location' =&gt; 'Users::login'));
        }
    }
));
</pre>
<p>The first parameter (<code>continue</code>) ensures that this route definition is treated as a <a href="https://gist.github.com/1512502" rel="nofollow" >continuation route</a>. This is because we don&#8217;t want to interrupt any normal route / parameter processing in this definition. We just wanna &#8220;grab&#8221; all calls to any <code>edit</code> action, and check (using <code>Auth</code>) for a valid user. If none is found, we <strong>process</strong> the request by returning a <code>Response</code>, which in the end redirects the user to the login page. If there is indeed a logged in user, the router will continue looking for other route definitions to match the request. So now all <code>edit</code> actions require a logged in user. Cool.</p>
<p>Next in our list: &#8220;<i>When accessing either the <code>Users::edit</code> or <code>Users::view</code> action, there should be an ID specified as a route parameter, and it should match an existing <code>User</code> record.</i>&#8221; Add the following to the <code>routes.php</code> file, below the content we just added:</p>
<pre class="brush:php">
Router::connect('/{:controller:users}/{:action:edit|view}/{:id:\d*}', array('id' =&gt; null), function($request) {
    if (empty($request-&gt;params['id'])) {
        throw new RoutingException('Missing ID');
    } elseif (!Users::first(array('conditions' =&gt; array('id' =&gt; $request-&gt;params['id'])))) {
        throw new RoutingException('Invalid ID');
    }
});
</pre>
<p>We are now getting more serious. In this definition, we are only matching the <code>Users</code> controller, and actions named either <code>edit</code> or <code>view</code>, which may or may not contain an <code>id</code> parameter. The route handler first checks to make sure the <code>id</code> parameter is given (if not, a <code>RoutingException</code> is thrown.) If the parameter is specified, it is used to find a matching <code>User</code> record with the given ID. If none is found, yet another <code>RoutingException</code> is thrown (you may wish to do something different here, like ensuring a 404 status). If the user is found, the route is not handled, which means some other route definition will handle it (the default route, in this case.)</p>
<p>The final checkpoint we have is: &#8220;<i>When accessing the <code>Users::edit</code> action, the given user should match the currently logged in user.</i>&#8221; So add the following to the <code>routes.php</code> file, below the content we just added:</p>
<pre class="brush:php">
Router::connect('/{:controller:users}/{:action:edit}/{:id:\d+}', array('id' =&gt; null), function($request) {
    $user = Auth::check('default');
    if ($user['id'] != $request-&gt;params['id']) {
        throw new RoutingException('You can only edit your own account');
    }
    return $request;
});
</pre>
<p>This defines a specific match to the <code>Users::edit</code> action with a set <code>id</code> parameter. We use that parameter to make sure it matches the ID of the logged in user. If it doesn&#8217;t match, we throw a <code>RoutingException</code>. If it does match, we return the request as we have successfully processed it.</p>
<p>You can now try accessing the edit and view actions using different scenarios: with a logged in user, while being logged out, editing a user which is not the current logged in user, etc. Everything should be nicely protected. And yet our controller code remained untouched. Nice, huh? That&#8217;s routing middleware for you. <img src='http://marianoiglesias.com.ar/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>No related posts.</p>]]></content:encoded>
			<wfw:commentRss>http://marianoiglesias.com.ar/php/route-middleware-with-lithium/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Book release: CakePHP 1.3 Application Development Cookbook</title>
		<link>http://marianoiglesias.com.ar/cakephp/book-release-cakephp-application-development-cookbook/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=book-release-cakephp-application-development-cookbook</link>
		<comments>http://marianoiglesias.com.ar/cakephp/book-release-cakephp-application-development-cookbook/#comments</comments>
		<pubDate>Wed, 30 Mar 2011 22:49:58 +0000</pubDate>
		<dc:creator>mariano</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://marianoiglesias.com.ar/?p=221</guid>
		<description><![CDATA[Just a few days ago, I was happy to see my first book published. Entitled CakePHP 1.3 Application Development Cookbook, it&#8217;s a book released in the form of a cookbook, with a series of solutions to common problems one faces when developing CakePHP applications. While working on it, I tried to aim for developers at [...]
No related posts.]]></description>
			<content:encoded><![CDATA[<p>Just a few days ago, I was happy to see my first book published. Entitled <a href="http://www.amazon.com/dp/1849511926/?tag=packtpubli-20" rel="nofollow" >CakePHP 1.3 Application Development Cookbook</a>, it&#8217;s a book released in the form of a cookbook, with a series of solutions to common problems one faces when developing <a href="http://cakephp.org" rel="nofollow" >CakePHP</a> applications.</p>
<p><a href="http://www.amazon.com/dp/1849511926/?tag=packtpubli-20" rel="nofollow" ><img class="alignleft size-medium wp-image-222" style="margin-right: 10px;" title="CakePHP 1.3 Application Development Cookbook cover" src="http://marianoiglesias.com.ar/wp-content/uploads/2011/03/book-medium-243x300.png" alt="" width="194" height="240" /></a>While working on it, I tried to aim for developers at different levels of knowledge, yet a disclaimer has to be made: this is <strong>not a beginners book</strong>. It will not teach you how to install CakePHP, or how to get its friendly URLs working on Microsoft platforms (dodged that bullet.) It is written for CakePHP developers that are looking to solve different problems, and leverage their own applications. So no &#8220;building a blog&#8221; chapter in this book.</p>
<p>There are some recipes that deal with more complex topics, while others deal with what I consider interesting solutions to simple problems. Each recipe starts by proposing a problem, showing the solution, and giving an explanation of how the solution works. Most of the recipes include alternatives and extend the topic at hand beyond the scope of the problem they are solving, and some of them are based on open source packages that CakePHP community members (myself included) released.</p>
<p>This book also benefited from an unbelievably, super-cool, top of the world team of technical reviewers (the <a href="http://mark-story.com" rel="nofollow" >CakePHP 1.3 lead developer</a> happens to be amongst them) that made its code shine (I am known for being humble.) They improved each recipe and proposed awesome alternatives to my original ideas. Because of that, this blog post is being written. I&#8217;m not sure sure I would&#8217;ve been as proud of the original version of the book <img src='http://marianoiglesias.com.ar/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
<p>You should also know that the publishing company behind the publication of this book, <a href="https://www.packtpub.com" rel="nofollow" >Packt Publishing</a>, is donating an important portion of the book earnings to the <a href="http://cakefoundation.org" rel="nofollow" >Cake Software Foundation</a>, which is like <em>The Force</em> behind CakePHP. So I might not get rich, but at least the foundation will get some beers out of each sale. And trust me, nothing says thank you like a beer.</p>
<p>If you bought the book, I welcome any feedback you may have (you could also <a href="http://www.amazon.com/dp/1849511926/?tag=packtpubli-20" rel="nofollow" >leave a review</a> and tell others how super cool the book is.) If you are more of a bytes person and less of a paper person, and look forward to reading the <strong>digital version</strong>, you can also get <a href="http://link.packtpub.com/6ihp7k" rel="nofollow" >the ebook</a>.</p>
<p>No related posts.</p>]]></content:encoded>
			<wfw:commentRss>http://marianoiglesias.com.ar/cakephp/book-release-cakephp-application-development-cookbook/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
		<item>
		<title>Let your MySQL partition breathe</title>
		<link>http://marianoiglesias.com.ar/php/let-your-mysql-partition-breathe/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=let-your-mysql-partition-breathe</link>
		<comments>http://marianoiglesias.com.ar/php/let-your-mysql-partition-breathe/#comments</comments>
		<pubDate>Sat, 28 Feb 2009 22:34:00 +0000</pubDate>
		<dc:creator>mariano</dc:creator>
				<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://marianoiglesias.com.ar/1/let-your-mysql-partition-breathe/</guid>
		<description><![CDATA[Today I noticed my MySQL partition was taking over 86 GB of the available 120 GB. So I got worried and I wrote this little script to tell me how much space each DB I have is taking: &#60;?php function format($size) { $unit = 'B'; $units = array( 'GB' =&#62; 1024 * 1024 * 1024 [...]
No related posts.]]></description>
			<content:encoded><![CDATA[<p>
Today I noticed my MySQL partition was taking over 86 GB of the available 120 GB. So I got worried and I wrote this little script to tell me how much space each DB I have is taking:
</p>
<pre class="brush:php">&lt;?php
function format($size) {
	$unit = 'B';
	$units = array(
		'GB' =&gt; 1024 * 1024 * 1024
		, 'MB' =&gt; 1024 * 1024
		, 'KB' =&gt; 1024
	);

	foreach($units as $currentUnit =&gt; $value) {
		if ($size &gt; 2 * $value) {
			$size /= $value;
			$unit = $currentUnit;
			break;
		}
	}

	return number_format($size, 1) . ' ' . $unit;
}

$settings = array(
	'host' =&gt; 'localhost'
	, 'user' =&gt; 'root'
	, 'password' =&gt; 'password'
);

$databases = array();

mysql_connect($settings['host'], $settings['user'], $settings['password']);

$result = mysql_query('show databases');
while($row = mysql_fetch_array($result)) {
	$databases[] = $row['Database'];
}

foreach($databases as $database) {
	$sizes[$database] = 0;

	mysql_select_db($database);
	$result = mysql_query('show table status');
	while($row = mysql_fetch_array($result)) {
		$sizes[$database] += $row['Data_length'] + $row['Index_length'];
	}
}

mysql_close();

foreach($sizes as $database =&gt; $size) {
	echo $database . ' = ' . format($size) . '&lt;br /&gt;';
}

echo '&lt;br /&gt;TOTAL: ' . format(array_sum($sizes));

?&gt;</pre>
<p>
When I ran it, I saw all my DBs where taking a total of 10.8 GB, much less than the 86 GB occupied in the partition. So it hit me, it has to be the binary logs, a set of files that store log events (<a href="http://dev.mysql.com/doc/refman/5.0/en/binary-log.html" rel="nofollow" >more about them here</a>). Indeed, if you would list the contents of /var/lib/mysql I would find a ton of .bin files, a lot of them as old as from 2007. Therefore, I realized I had to flush the logs.
</p>
<p>
In order to flush the binary logs, I logged in to the MySQL console as an administrator, and issued (if you are on a server with replication, you want to <a href="http://dev.mysql.com/doc/refman/5.0/en/purge-binary-logs.html" rel="nofollow" >purge the binary logs</a> instead):
</p>
<pre class="brush:sql">FLUSH LOGS;
RESET MASTER;</pre>
<p>After doing so, the MySQL partition is now taking a total of 12 GB. Much better!</p>
<p>No related posts.</p>]]></content:encoded>
			<wfw:commentRss>http://marianoiglesias.com.ar/php/let-your-mysql-partition-breathe/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>MSN Search API PHP Client</title>
		<link>http://marianoiglesias.com.ar/php/msn-search-api-php-client/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=msn-search-api-php-client</link>
		<comments>http://marianoiglesias.com.ar/php/msn-search-api-php-client/#comments</comments>
		<pubDate>Thu, 15 Sep 2005 19:34:00 +0000</pubDate>
		<dc:creator>mariano</dc:creator>
				<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://marianoiglesias.com.ar/1/msn-search-api-php-client/</guid>
		<description><![CDATA[Microsoft has recently published their own MSN Search API (now competing with Google and Yahoo) implemented as a SOAP Web Service. After some deliberation on the MSN Search API forums, I managed to develop a small PHP class to handle the API (thanks to quodlibet for his help identifying a small bug.) The class uses [...]
No related posts.]]></description>
			<content:encoded><![CDATA[<p>
Microsoft has recently published their own <a href="http://msdn.microsoft.com/msn/msnsearch/" rel="nofollow" >MSN Search API</a> (now competing with Google and Yahoo) implemented as a SOAP Web Service. After some deliberation on the MSN Search API forums, I managed to develop a small PHP class to handle the API (thanks to <a href="http://www.quodlibet.be/" rel="nofollow" >quodlibet</a> for his help identifying a small bug.)
</p>
<p>
The class uses <a href="http://dietrich.ganx4.com/nusoap/" rel="nofollow" >nuSOAP</a> for interacting with the SOAP server. You will need to download nuSOAP and copy it inside a folter named <code>nusoap</code>, located in the same location as this class&#8217; file.
</p>
<p><span id="more-55"></span></p>
<p>
The source code for the class is the following (save it as <code>MSNWebSearch.class.php</code>):
</p>
<pre class="brush:php">&lt;?php

define ('NUSOAP_PATH', dirname(__FILE__) . '/nusoap');
define ('MSN_API_KEY', 'Insert here your default MSN API key');
define ('MSN_API_ENDPOINT', 'http://soap.search.msn.com/webservices.asmx');
define ('MSN_API_NAMESPACE', 'http://schemas.microsoft.com/MSNSearch/2005/09/fex');

require_once(NUSOAP_PATH . '/nusoap.php');

class MSNWebSearch
{
	var $apiKey;
	var $filterAdult;
	var $languages;
	var $page;
	var $pages;
	var $query;
	var $records;
	var $recordsPerPage;

	function &amp;MSNWebSearch($apiKey = null)
	{
		$this-&gt;apiKey = isset($apiKey) ? $apiKey : MSN_API_KEY;
		$this-&gt;filterAdult = false;
		$this-&gt;languages = 'en-US';
		$this-&gt;recordsPerPage = 10;
		$this-&gt;page = 1;
	}

	function getApiKey()
	{
		return $this-&gt;apiKey;
	}

	function setApiKey($apiKey)
	{
		$this-&gt;apiKey = $apiKey;
	}

	function getErrorMessage()
	{
		return $this-&gt;errorMessage;
	}

	function getFilterAdult()
	{
		return $this-&gt;filterAdult;
	}

	function setFilterAdult($filterAdult)
	{
		$this-&gt;filterAdult = $filterAdult;
	}

	function getLanguages()
	{
		return $this-&gt;languages;
	}

	function setLanguages($languages)
	{
		$this-&gt;languages = $languages;
	}

	function getPage()
	{
		return $this-&gt;page;
	}

	function setPage($page)
	{
		if ($page &lt; 1)
		{
			$page = 1;
		}

		$this-&gt;page = $page;
	}

	function getPages()
	{
		return $this-&gt;pages;
	}

	function getQuery()
	{
		return $this-&gt;query;
	}

	function setQuery($query)
	{
		$this-&gt;query = $query;
	}

	function getRecords()
	{
		return $this-&gt;records;
	}

	function getRecordsPerPage()
	{
		return $this-&gt;recordsPerPage;
	}

	function setRecordsPerPage($recordsPerPage)
	{
		$this-&gt;recordsPerPage = is_int($recordsPerPage) &amp;&amp; $recordsPerPage &gt; 0 ? $recordsPerPage : 10;
	}

	function get()
	{
		$startIndex = ($this-&gt;page - 1) * $this-&gt;recordsPerPage;
		$elementsCount = $this-&gt;recordsPerPage;

		$parameters = array(
			'AppID' =&gt; $this-&gt;apiKey,
			'Query' =&gt; $this-&gt;query,
			'CultureInfo' =&gt; $this-&gt;languages,
			'SafeSearch' =&gt; ($this-&gt;filterAdult ? 'Strict' : 'Off'),
			'Requests' =&gt; array (
				'SourceRequest' =&gt; array (
					'Source' =&gt; 'Web',
					'Offset' =&gt; $startIndex,
					'Count' =&gt; $elementsCount,
					'ResultFields' =&gt; 'All'
				)
			)
		);

		if (isset($this-&gt;country))
		{
			$parameters['Location'] = $this-&gt;country;
		}

		$soapClient =&amp; new soapclient(MSN_API_ENDPOINT);

		$soapResult = $soapClient-&gt;call('Search', array ('Request' =&gt; $parameters), MSN_API_NAMESPACE );

		if ($soapClient-&gt;getError())
		{
			$this-&gt;errorMessage = $soapClient-&gt;getError();

			return false;
		}

		$this-&gt;records = $soapResult['Responses']['SourceResponse']['Total'];
		$this-&gt;pages = ceil($this-&gt;records / $this-&gt;recordsPerPage);

		if (is_array($soapResult['Responses']['SourceResponse']['Results']))
		{
			$result = array();

			foreach ($soapResult['Responses']['SourceResponse']['Results'] as $item)
			{
				$result[] = array (
					'url' =&gt; $item['Url'],
					'urlDisplay' =&gt; $item['DisplayUrl'],
					'urlCache' =&gt; $item['CacheUrl'],
					'title' =&gt; isset($item['Title']) &amp;&amp; trim($item['Title']) != '' ? $item['Title'] : $item['DisplayUrl'],
					'snippet' =&gt; $item['Description']
				);
			}
		}
		else
		{
			$result = array();
		}

		return $result;
	}
}
?&gt;</pre>
<p>
The usage of this class is quite simple. Take a look at the following <code>index.php</code> file to see an example. You can see it <a href="http://www.cricava.com/blogs/media/el_eternauta/sources/msn/index.php" rel="nofollow" >working here</a>. Please note that where it says <code>'My MSN API Key'</code>, you need to insert your own MSN API Key. If you don&#8217;t have one, <a href="http://search.msn.com/developer/appids.aspx?FORM=PMPD2" rel="nofollow" >get one now</a>.
</p>
<pre class="brush:php">&lt;?php
require_once(dirname(__FILE__) . '/MSNWebSearch.class.php');

if (isset($_GET) &amp;&amp; isset($_GET['query']) &amp;&amp; trim($_GET['query']) != '')
{
	$currentPage = 1;

	if (isset($_GET['page']))
	{
		$currentPage = $_GET['page'];
	}

	$msnSearch =&amp; new MSNWebSearch();

	$msnSearch-&gt;setApiKey('My MSN API Key');
	$msnSearch-&gt;setQuery($_GET['query']);
	$msnSearch-&gt;setPage($currentPage);

	$result =&amp; $msnSearch-&gt;get();

	if ($result !== false)
	{
		if ($msnSearch-&gt;getPages() &gt; 1)
		{
			$navigation = array();

			$navigation['pages'] = $msnSearch-&gt;getPages();

			if ($msnSearch-&gt;getPage() &gt; 1)
			{
				$navigation['back'] = $PHP_SELF . '?page=' . ($msnSearch-&gt;getPage() - 1) . '&amp;query=' . urlencode($msnSearch-&gt;getQuery());
			}

			if ($msnSearch-&gt;getPage() &lt; $msnSearch-&gt;getPages())
			{
				$navigation['next'] = $PHP_SELF . '?page=' . ($msnSearch-&gt;getPage() + 1) . '&amp;query=' . urlencode($msnSearch-&gt;getQuery());
			}
		}
	}
}
?&gt;
&lt;html&gt;
	&lt;head&gt;
		&lt;titlevMSN Web Search&lt;/title&gt;
	&lt;/head&gt;
	&lt;body&gt;
		&lt;center&gt;&lt;p&gt;
			&lt;form action="&lt;?php echo $PHP_SELF; ?&gt;" method="GET"&gt;
				Search:
				&lt;input type="text" name="query" size="70" value="&lt;?php echo isset($_GET['query']) ? $_GET['query'] : ''; ?&gt;" /&gt;
				&lt;input type="submit" value="Search" /&gt;
				&lt;?php
				if (isset($msnSearch) &amp;&amp; $result !== false)
				{
				?&gt;
				&lt;br /&gt;
				&lt;b&gt;&lt;?php echo $msnSearch-&gt;getRecords(); ?&gt;&lt;/b&gt; records matched your query.
				&lt;?php
				}
				?&gt;
			&lt;/form&gt;
		&lt;/p&gt;&lt;/center&gt;
		&lt;?php
		if (isset($msnSearch) &amp;&amp; $result === false)
		{
			?&gt;
			There was an error with your search. Error message: &lt;b&gt;&lt;?php echo $msnSearch-&gt;getErrorMessage(); ?&gt;&lt;/b&gt;.
			&lt;?php
		}
		else if (isset($msnSearch))
		{
			if (isset($navigation))
			{
			?&gt;
			&lt;center&gt;&lt;p&gt;
				Pages: &lt;b&gt;&lt;?php echo $msnSearch-&gt;getPages(); ?&gt;&lt;/b&gt;
				&lt;?php
				if (isset($navigation['back']))
				{
				?&gt;
				&lt;a href="&lt;?php echo $navigation['back']; ?&gt;"&gt;Previous&lt;/a&gt;
				&lt;?php
				}

				if (isset($navigation['back']) &amp;&amp; isset($navigation['next']))
				{
				?&gt;
				|
				&lt;?php
				}

				if (isset($navigation['next']))
				{
				?&gt;
				&lt;a href="&lt;?php echo $navigation['next']; ?&gt;"&gt;Next&lt;/a&gt;
				&lt;?php
				}
				?&gt;
			&lt;/p&gt;&lt;/center&gt;
			&lt;?php
			}
			?&gt;
			&lt;ul&gt;
			&lt;?php
			foreach ($result as $item)
			{
			?&gt;
			&lt;li&gt;
				&lt;a href="&lt;?php echo $item['url']; ?&gt;"&gt;&lt;?php echo $item['title']; ?&gt;&lt;/a&gt;: &lt;?php echo $item['snippet']; ?&gt;
				&lt;br /&gt;
				&lt;font color="#008000"&gt;&lt;?php echo $item['urlDisplay']; ?&gt;&lt;/font&gt; - &lt;a href="&lt;?php echo $item['urlCache']; ?&gt;"&gt;Cache&lt;/a&gt;
			&lt;/li&gt;
			&lt;?php
			}
			?&gt;
			&lt;/ul&gt;
			&lt;?php
		}
		?&gt;
	&lt;/body&gt;
&lt;/html&gt;</pre>
<p>
Hope this helps anyone.
</p>
<p>
<b>UPDATE [Aug 20, 2006]</b>: After receiving some emails saying that the class didn&#8217;t work (which in fact does), I decided to publish a ZIP file that contains a working MSN Search Based Page. It includes nuSOAP as of July 19, 2006. First, <a href="http://www.cricava.com/blogs/media/el_eternauta/sources/msn.zip" rel="nofollow" >download the ZIP file</a>. Then, uncompress to a new directory on your webserver and EDIT the file index.php, looking for line 3. Insert your MSN API KEY there. After specifying your API key, you should be good to go for testing.</p>
<p>No related posts.</p>]]></content:encoded>
			<wfw:commentRss>http://marianoiglesias.com.ar/php/msn-search-api-php-client/feed/</wfw:commentRss>
		<slash:comments>17</slash:comments>
		</item>
		<item>
		<title>Get a taste of AJAX with PHP</title>
		<link>http://marianoiglesias.com.ar/php/get-a-taste-of-ajax-with-php/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=get-a-taste-of-ajax-with-php</link>
		<comments>http://marianoiglesias.com.ar/php/get-a-taste-of-ajax-with-php/#comments</comments>
		<pubDate>Fri, 19 Aug 2005 18:57:00 +0000</pubDate>
		<dc:creator>mariano</dc:creator>
				<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://marianoiglesias.com.ar/1/get-a-taste-of-ajax-with-php/</guid>
		<description><![CDATA[Some of you may have heard of Ajax, while some may have not. The truth of the matter is that Ajax (Asynchronous JavaScript and XML) is a technology to be followed. Furthermore, a technology to be used! To give a little background about it, let&#8217;s see what wikipedia has to say about it: &#8220;term describing [...]
No related posts.]]></description>
			<content:encoded><![CDATA[<p>Some of you may have heard of Ajax, while some may have not. The truth of the matter is that Ajax (Asynchronous JavaScript and XML) is a technology to be followed. Furthermore, a technology to be used! To give a little background about it, let&#8217;s see what <a href="http://en.wikipedia.org/wiki/AJAX" rel="nofollow" >wikipedia</a> has to say about it: &#8220;term describing a web development technique for creating interactive web applications using a combination of: HTML (or XHTML) and CSS for presenting information; The Document Object Model manipulated through JavaScript to dynamically display and interact with the information presented; and The XMLHttpRequest object to exchange data asynchronously with the web server. (XML is commonly used, although any text format will work, including preformatted HTML, plain text, and JSON)&#8221;.</p>
<p>For Gmail (Google Mail) or GMaps (Google Maps) users, you have already experienced the power of Ajax. Notice how when you switch from page to page in GMail the browser page does not reload? That&#8217;s the power of Ajax.</p>
<p>In this post, I&#8217;ll give you an introduction to Ajax using a yet in beta stage, but very powerful open source PHP library called <a href="http://xajax.sourceforge.net/" rel="nofollow" >XAJAX</a>. I&#8217;ll get right down to the coding (more information about this library can be obtained from <a href="http://xajax.sourceforge.net/" rel="nofollow" >its sourceforge site</a>.)</p>
<p>As an example, I&#8217;ll develop the classic Country / State combo box combination. Normally, if we wanted the State combo to be updated by the current selected country, we would make one of two choices:</p>
<ul>
<li>Use Javascript by sending all countries and their respective provinces to the client browser, and update State combobox choosing those states that belong to the selected country.</li>
<li>Reload the page when a different country has been selected, including only that country&#8217;s states in the State combo box.</li>
</ul>
<p>With Ajax, we&#8217;ll do none of those. We&#8217;ll have the list of states per country on a server-side PHP script. When the country selection changes, we&#8217;ll use Xajax to update the list of available states based on the country selection. To simplify, I&#8217;ll do all this on the same PHP file.</p>
<p>We start by including the xanax library, and declaring a PHP function that will be called via Ajax when the country selection changes.</p>
<pre class="brush:php">require_once(dirname(__FILE__) . '/xajax.inc.php');

function setCountry($countryCode)
{
        $provinces = array();

        switch($countryCode)
        {
                case 'AR':

                        $provinces['BA'] = 'Buenos Aires';
                        $provinces['CO'] = 'Cordoba';
                        $provinces['SL'] = 'Salta';
                        $provinces['SF'] = 'Santa Fe';

                        break;

                case 'US':

                        $provinces['CA'] = 'California';
                        $provinces['WA'] = 'Washington';

                        break;
        }

        $objResponse =&amp; new xajaxResponse();

        if (count($provinces) &gt; 0)
        {
                $objResponse-&gt;addScript("document.getElementById('province').disabled = false;");
                $objResponse-&gt;addScript("document.getElementById('province').options.length = 0;");

                foreach ($provinces as $id =&gt; $province)
                {
                        $objResponse-&gt;addScript("addOption('province','" . $province . "','" . $id . "');");
                }
        }
        else
        {
                $objResponse-&gt;addScript("document.getElementById('province').options.length = 0;");
                $objResponse-&gt;addScript("addOption('province','-- Select a Country first -','');");
                $objResponse-&gt;addScript("document.getElementById('province').disabled = true;");
        }

        return $objResponse-&gt;getXML();
}</pre>
<p>The first few lines are quite easy to follow. The first interesting thing that we see here is when we start by creating the Xajax response (<code>$objResponse =&amp; new xajaxResponse();</code>) After that, if there are states to be inserted, we enable the state control, reset the state combo box (by setting its option length to 0), and for each state we call a client side javascript function called <code>addOption()</code> that will insert the specified option in the combo box. If no states are to be inserted, then we empty the combo box, inserting only a dummy option, and disable the element.</p>
<p><span id="more-58"></span></p>
<p>Now, let&#8217;s see the next few server-side lines of code:</p>
<pre class="brush:php">$xajax =&amp; new xajax();

$xajax-&gt;registerFunction("setCountry");

$xajax-&gt;processRequests();</pre>
<p>Here, we instantiate the main xajax handler, registering the function we&#8217;ve just created, and telling xajax to process any request. From here on, all the remaining code is client side (HTML). What we need to pay special attention to is the client side javascript function we already mentioned, which is included here, and the insertion of xajax generated javascript code, all of this is done within the <code>header</code> tag of the HTML document:</p>
<pre class="brush:php">&lt;?php
$xajax-&gt;printJavascript();
?&gt;
&lt;script type="text/javascript"&gt;
&lt;!--
function addOption(selectId, txt, val)
{
        var objOption = new Option(txt,val);

        document.getElementById(selectId).options.add(objOption);
}
// --&gt;
&lt;/script&gt;</pre>
<p>Finally, the code that dispatches xajax when the country selection changes:</p>
<pre class="brush:html">&lt;select name="country" id="country" onchange="xajax_setCountry(document.getElementById('country').value);"&gt;
&lt;option value=""&gt;-- Select a Country --&lt;/option&gt;
&lt;option value="AR"&gt;Argentina&lt;/option&gt;
&lt;option value="US"&gt;United States&lt;/option&gt;
&lt;/select&gt;</pre>
<p>You can see the code <a href="http://cricava.com/blogs/media/el_eternauta/sources/xajax/index.php" rel="nofollow" >working here</a>, and download the <a href="http://cricava.com/blogs/media/el_eternauta/sources/xajax/example-xajax.txt" rel="nofollow" >source file here</a>.</p>
<p>No related posts.</p>]]></content:encoded>
			<wfw:commentRss>http://marianoiglesias.com.ar/php/get-a-taste-of-ajax-with-php/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
	</channel>
</rss>

<!-- Performance optimized by W3 Total Cache. Learn more: http://www.w3-edge.com/wordpress-plugins/

Page Caching using disk: enhanced (User agent is rejected)
Database Caching 15/40 queries in 0.014 seconds using disk: basic
Object Caching 1097/1176 objects using disk: basic

Served from: marianoiglesias.com.ar @ 2012-02-03 23:40:21 -->
