<?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>techfounder &#187; PHP</title>
	<atom:link href="http://www.techfounder.net/category/webdev/php/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.techfounder.net</link>
	<description>Blog about web development and Internet entrepreneurship</description>
	<lastBuildDate>Mon, 22 Aug 2011 08:36:24 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.1</generator>
		<item>
		<title>Breaking down the PayPal API</title>
		<link>http://www.techfounder.net/2011/04/23/breaking-down-the-paypal-api/</link>
		<comments>http://www.techfounder.net/2011/04/23/breaking-down-the-paypal-api/#comments</comments>
		<pubDate>Sat, 23 Apr 2011 20:43:44 +0000</pubDate>
		<dc:creator>Eran Galperin</dc:creator>
				<category><![CDATA[Binpress]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Web development]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[direct payment]]></category>
		<category><![CDATA[express checkout]]></category>
		<category><![CDATA[paypal]]></category>

		<guid isPermaLink="false">http://www.techfounder.net/?p=654</guid>
		<description><![CDATA[PayPal is the most popular platform for receiving online payments. The relative ease of opening a PayPal account and receiving payments compared to opening a merchant account for a traditional payment gateway is the number one reason, and another is the comprehensive API they provide for their payment services. Foreclosure: The PayPal API is amongst [...]]]></description>
			<content:encoded><![CDATA[<p>PayPal is the most popular platform for receiving online payments. The relative ease of opening a PayPal account and receiving payments compared to opening a merchant account for a traditional payment gateway is the number one reason, and another is the comprehensive API they provide for their payment services.</p>
<p>Foreclosure: The PayPal API is amongst the worst I've ever had to deal with - inconsistencies, sometimes poor and conflicting documentation, unpredictable failures and account changes and major differences between the live and sandbox versions all conspire to make working with the PayPal API quite a pain in the ass.</p>
<p>Unfortunately, there doesn't seem to be any better alternatives currently, so hopefully this guide will help ease the pain for some of you out there taking your lumps working the API into your applications.<br />
<span id="more-654"></span></p>
<h2>The different payment options</h2>
<p>PayPal offers a variety of payment options, which might be confusing at first:</p>
<ul>
<li><strong>Express Checkout</strong> - The premier PayPal service. Express checkout allows you to receive payments without having a merchant account or meeting special requirements other than having your account verified (either via a bank account or a credit card).Previously you could receive Express Checkout payments from PayPal users only, but PayPal has since added a credit-card option for non-PayPal users making this service accessible to practically anyone with a major credit-card.Note that the Express Checkout process occurs on the PayPal platform and thus can never be fully integrated into your site experience completely.</li>
<li><strong>Direct Payment </strong>- The Direct Payment method allows you to receive credit-card payments directly through an API call. This allows you to host the payment process on your site in full, which might make for a more complete shopping experience for shoppers.The Direct Payment method has several variations that allow you to authorize a payment and complete it at a later date - the appropriately named methods Authorization and Capture. The direct payments methods are a part of the <a href="https://merchant.paypal.com/cgi-bin/marketingweb?cmd=_render-content&amp;content_ID=merchant/wp_pro&amp;nav=2.1.1" target="_blank">Website Payments Pro</a> API, which is only available U.S accounts.</li>
<li><strong>Recurring Payments</strong> - allow you to set up a recurring transaction - i.e, a subscription payment.</li>
<li><strong>Mass Payments</strong> - allow you to transfer money to multiple accounts at once.</li>
</ul>
<p>This is a comprehensive list, but it covers the main payment options (<a href="https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&amp;content_ID=developer/howto_api_reference" target="_blank">see the docs for more</a>).</p>
<h3>Making API requests</h3>
<p>PayPal supports two main formats over HTTP - NVP and SOAP. NVP is short for Node-Value-Pairs and SOAP stands for Simple Object Access Protocol. I will cover the NVP approach which I prefer to SOAP's comparatively verbose and general complex syntax.</p>
<p>Each of the API methods have different parameters but they all share <a href="https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&amp;content_ID=developer/e_howto_api_nvp_NVPAPIOverview#id09C2F0G0C7U" target="_blank">some basic parameters</a> which are used to identify the API account and sign the transaction. Those include:</p>
<ul>
<li>USER - Your PayPal API Username.</li>
<li>PWD - Your PayPal API Password.</li>
<li>VERSION - Version number of the NVP API service, such as 56.0.</li>
<li>SIGNATURE - Your PayPal API signature string. This parameter is optional if you use a certificate to authenticate</li>
</ul>
<p>The last required parameter is METHOD which declares which API method we are calling.</p>
<p>Requests are made over HTTPS. We'll use cURL to build up our basic request, and encapsulate the process in a function:</p>
<pre class="php"><span style="color: #000000; font-weight: bold;">function</span> paypalApiRequest<span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">$method</span>,<span style="color: #0000ff;">$params</span> = <a href="http://www.php.net/array"><span style="color: #000066;">array</span></a><span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#123;</span>
	<span style="color: #b1b100;">if</span><span style="color: #66cc66;">&#40;</span> <a href="http://www.php.net/empty"><span style="color: #000066;">empty</span></a><span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">$method</span><span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#123;</span> <span style="color: #808080; font-style: italic;">//Check if API method is not empty</span>
		<span style="color: #b1b100;">return</span> <span style="color: #000000; font-weight: bold;">false</span>;
	<span style="color: #66cc66;">&#125;</span>
&nbsp;
	<span style="color: #808080; font-style: italic;">//Our request parameters</span>
	<span style="color: #0000ff;">$requestParams</span> = <a href="http://www.php.net/array"><span style="color: #000066;">array</span></a><span style="color: #66cc66;">&#40;</span>
		<span style="color: #ff0000;">'METHOD'</span> =&gt; <span style="color: #0000ff;">$method</span>,
		<span style="color: #ff0000;">'VERSION'</span> =&gt; <span style="color: #ff0000;">'65.1'</span>,
		<span style="color: #ff0000;">'PWD'</span> =&gt; <span style="color: #ff0000;">'ourAPIpassword'</span>,
		<span style="color: #ff0000;">'USER'</span> =&gt; <span style="color: #ff0000;">'ourAPIusername'</span>,
		<span style="color: #ff0000;">'SIGNATURE'</span> =&gt; <span style="color: #ff0000;">'ourAPIsignature'</span>
	<span style="color: #66cc66;">&#41;</span>;
	<span style="color: #808080; font-style: italic;">//Building our NVP string</span>
	<span style="color: #0000ff;">$request</span> = http_build_query<span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">$requestParams</span> + <span style="color: #0000ff;">$params</span><span style="color: #66cc66;">&#41;</span>;
&nbsp;
	<span style="color: #808080; font-style: italic;">//cURL settings</span>
	<span style="color: #0000ff;">$curlOptions</span> = <a href="http://www.php.net/array"><span style="color: #000066;">array</span></a> <span style="color: #66cc66;">&#40;</span>
		CURLOPT_URL =&gt; <span style="color: #0000ff;">$this</span> -&gt; <span style="color: #006600;">getOption</span><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">'endpoint'</span><span style="color: #66cc66;">&#41;</span>,
		CURLOPT_VERBOSE =&gt; <span style="color: #cc66cc;">1</span>,
		CURLOPT_SSL_VERIFYPEER =&gt; <span style="color: #000000; font-weight: bold;">true</span>,
		CURLOPT_SSL_VERIFYHOST =&gt; <span style="color: #cc66cc;">2</span>,
		CURLOPT_CAINFO =&gt; <a href="http://www.php.net/dirname"><span style="color: #000066;">dirname</span></a><span style="color: #66cc66;">&#40;</span><span style="color: #000000; font-weight: bold;">__FILE__</span><span style="color: #66cc66;">&#41;</span> . <span style="color: #ff0000;">'/cacert.pem'</span>, <span style="color: #808080; font-style: italic;">//CA cert file</span>
		CURLOPT_RETURNTRANSFER =&gt; <span style="color: #cc66cc;">1</span>,
		CURLOPT_POST =&gt; <span style="color: #cc66cc;">1</span>,
		CURLOPT_POSTFIELDS =&gt; <span style="color: #0000ff;">$request</span>
	<span style="color: #66cc66;">&#41;</span>;
&nbsp;
	<span style="color: #0000ff;">$ch</span> = curl_init<span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span>;
	curl_setopt_array<span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">$ch</span>,<span style="color: #0000ff;">$curlOptions</span><span style="color: #66cc66;">&#41;</span>;
&nbsp;
	<span style="color: #808080; font-style: italic;">//Sending our request - $response will hold the API response</span>
	<span style="color: #0000ff;">$response</span> = curl_exec<span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">$ch</span><span style="color: #66cc66;">&#41;</span>;
&nbsp;
	<span style="color: #808080; font-style: italic;">//Checking for cURL errors</span>
	<span style="color: #b1b100;">if</span> <span style="color: #66cc66;">&#40;</span>curl_errno<span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">$ch</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#123;</span>
		<span style="color: #808080; font-style: italic;">//Handle errors</span>
	<span style="color: #66cc66;">&#125;</span> <span style="color: #b1b100;">else</span>  <span style="color: #66cc66;">&#123;</span>
		curl_close<span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">$ch</span><span style="color: #66cc66;">&#41;</span>;
		<span style="color: #0000ff;">$responseArray</span> = <a href="http://www.php.net/array"><span style="color: #000066;">array</span></a><span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span>;
		<a href="http://www.php.net/parse_str"><span style="color: #000066;">parse_str</span></a><span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">$response</span>,<span style="color: #0000ff;">$responseArray</span><span style="color: #66cc66;">&#41;</span>; <span style="color: #808080; font-style: italic;">// Break the NVP string to an array</span>
		<span style="color: #b1b100;">return</span> <span style="color: #0000ff;">$responseArray</span>;
	<span style="color: #66cc66;">&#125;</span>
<span style="color: #66cc66;">&#125;</span></pre>
<p>Note that I use a CA certificate file for SSL certificate validation. You can obtain the file from the <a href="http://curl.haxx.se/docs/caextract.html">cURL website</a> or any trusted source. Update the file to the certificate file according to where you placed it.</p>
<p>The response returned would be in NVP format as well. A parameter named <strong>ACK</strong> signifies the status of the request - <strong>Success</strong> or <strong>SuccessWithWarning </strong>on a successful request and <strong>Error </strong>or <strong>Warning </strong>values when the request failed. There are many reasons that a request could fail, different ones for each API method, and those are <a href="https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&amp;content_ID=developer/e_howto_api_nvp_errorcodes">covered in detail in the manual</a>. Keep in mind that the parameter values are case-sensitive so you should code against it accordingly.</p>
<h2>Express Checkout</h2>
<p>In this post I'll focus on the Express Checkout process and use it to illustrate how you can work with the API:</p>
<p>The Express Checkout process works as follows:</p>
<ul>
<li>We request a checkout token from PayPal using the order details</li>
<li>If successful, we redirect the user to the PayPal endpoint using the received token</li>
<li>User completes / cancels payment on the PayPal platform and is redirected back to our site</li>
<li>We complete the payment either when the user is redirected back or via IPN</li>
</ul>
<h3>1. Getting the checkout token - SetExpressCheckout</h3>
<p>We initiate the Express Checkout process by passing the order details to the PayPal API and receive a token string that identifies it. This token would be used in the next step to redirect to PayPal.</p>
<p>Required parameters:</p>
<ul>
<li>METHOD - 'SetExpressCheckout' (the API method we're using)</li>
<li>RETURNURL - The URL the user will be redirected to after payment process is complete</li>
<li>CANCELURL - The URL the user will be redirected to after cancelling the payment process</li>
<li>PAYMENTREQUEST_0_AMT - The transaction total amount.</li>
<li>PAYMENTREQUEST_0_ITEMAMT - The cost total of the items in the order, excluding shipping, taxes and other costs. If there are no extra costs, it should be the same value as PAYMENTREQUEST_0_AMT.</li>
</ul>
<p>There are additional parameters we can pass to add more information about the order, some of which have default values:</p>
<ul>
<li>PAYMENTREQUEST_0_CURRENCYCODE - Payment currency. A three-letter code, default is USD.</li>
<li>PAYMENTREQUEST_0_SHIPPINGAMT - Total shipping costs for this order</li>
<li>PAYMENTREQUEST_0_TAXAMT - Total tax amount for this order (required if per item tax is specified, see below)</li>
<li>PAYMENTREQUEST_0_DESC - Order description</li>
</ul>
<p>We can also add details about individual items in the order:</p>
<ul>
<li>L_PAYMENTREQUEST_0_NAMEm - Item name.</li>
<li>L_PAYMENTREQUEST_0_DESCm - Item description.</li>
<li>L_PAYMENTREQUEST_0_AMTm - Item cost</li>
<li>L_PAYMENTREQUEST_0_QTYm - Item quantity</li>
</ul>
<p>'m' is a variable index identifying the item (use the same number for all details of the same item).</p>
<p>There are many other optional options (<a href="https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&amp;content_ID=developer/e_howto_api_nvp_r_SetExpressCheckout" target="_blank">read more in the API docs</a>).</p>
<p>We'll use the function we wrote above for building the SetExpressCheckout request:</p>
<pre class="php"><span style="color: #808080; font-style: italic;">//Our request parameters</span>
<span style="color: #0000ff;">$requestParams</span> = <a href="http://www.php.net/array"><span style="color: #000066;">array</span></a><span style="color: #66cc66;">&#40;</span>
	<span style="color: #ff0000;">'RETURNURL'</span> =&gt; <span style="color: #ff0000;">'http://www.yourdomain.com/payment/success'</span>,
	<span style="color: #ff0000;">'CANCELURL'</span> =&gt; <span style="color: #ff0000;">'http://www.yourdomain.com/payment/cancelled'</span>
<span style="color: #66cc66;">&#41;</span>;
&nbsp;
<span style="color: #0000ff;">$orderParams</span> = <a href="http://www.php.net/array"><span style="color: #000066;">array</span></a><span style="color: #66cc66;">&#40;</span>
	<span style="color: #ff0000;">'PAYMENTREQUEST_0_AMT'</span> =&gt; <span style="color: #ff0000;">'500'</span>,
	<span style="color: #ff0000;">'PAYMENTREQUEST_0_SHIPPINGAMT'</span> =&gt; <span style="color: #ff0000;">'4'</span>
	<span style="color: #ff0000;">'PAYMENTREQUEST_0_CURRENCYCODE'</span> =&gt; <span style="color: #ff0000;">'GBP'</span>
<span style="color: #66cc66;">&#41;</span>;
&nbsp;
<span style="color: #0000ff;">$item</span> = <a href="http://www.php.net/array"><span style="color: #000066;">array</span></a><span style="color: #66cc66;">&#40;</span>
	<span style="color: #ff0000;">'L_PAYMENTREQUEST_0_NAME0'</span> =&gt; <span style="color: #ff0000;">'iPhone'</span>,
	<span style="color: #ff0000;">'L_PAYMENTREQUEST_0_DESC0'</span> =&gt; <span style="color: #ff0000;">'White iPhone, 16GB'</span>,
	<span style="color: #ff0000;">'L_PAYMENTREQUEST_0_AMT0'</span> =&gt; <span style="color: #ff0000;">'500'</span>,
	<span style="color: #ff0000;">'L_PAYMENTREQUEST_0_QTY0'</span> =&gt; <span style="color: #ff0000;">'1'</span>
<span style="color: #66cc66;">&#41;</span>;
<span style="color: #0000ff;">$response</span> = paypalApiRequest<span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">'SetExpressCheckout'</span>,<span style="color: #0000ff;">$requestParams</span> + <span style="color: #0000ff;">$orderParams</span> + <span style="color: #0000ff;">$item</span><span style="color: #66cc66;">&#41;</span>;</pre>
<h3>2. Redirecting to PayPal using the Checkout Express token</h3>
<p>If the request was successful, we'll receive a checkout token in the 'TOKEN' parameter of the response.</p>
<pre class="php"><span style="color: #b1b100;">if</span><span style="color: #66cc66;">&#40;</span><a href="http://www.php.net/is_array"><span style="color: #000066;">is_array</span></a><span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">$response</span><span style="color: #66cc66;">&#41;</span> &amp;&amp; <span style="color: #0000ff;">$response</span><span style="color: #66cc66;">&#91;</span><span style="color: #ff0000;">'ACK'</span><span style="color: #66cc66;">&#93;</span> == <span style="color: #ff0000;">'Success'</span><span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#123;</span> <span style="color: #808080; font-style: italic;">//Request successful</span>
      <span style="color: #0000ff;">$token</span> = <span style="color: #0000ff;">$response</span><span style="color: #66cc66;">&#91;</span><span style="color: #ff0000;">'TOKEN'</span><span style="color: #66cc66;">&#93;</span>;
      <a href="http://www.php.net/header"><span style="color: #000066;">header</span></a><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">'Location: https://www.paypal.com/webscr?cmd=_express-checkout&amp;token='</span> . <a href="http://www.php.net/urlencode"><span style="color: #000066;">urlencode</span></a><span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">$token</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span>;
<span style="color: #66cc66;">&#125;</span></pre>
<p>The user will now go through the purchase process on the PayPal site. If he confirms or cancels it he will return to one of the URLs we specified in the request.</p>
<h3>3. Completing the transaction</h3>
<p>Assuming the user confirmed the transaction, he will be redirected to our site by PayPal. At this point we have two relevant API methods we should use - 'DoExpressCheckoutPayment' will complete the transaction, but before that we might want to get additional information on the buyer using 'GetExpressCheckoutDetails'.</p>
<p>PayPal will redirect the user back from the purchase with the checkout token, which we will use to call those methods. The token will be available in the URL query parameters via the 'token' parameter. We will check for its existence in the confirmation URL, and send our API requests if we find it.</p>
<p>The 'GetExpressCheckoutDetails' method requires only the checkout token. 'DoExpressCheckoutPayment' requires a couple of additional parameters:</p>
<ul>
<li>PAYMENTREQUEST_0_PAYMENTACTION - Payment action. Should be set to 'Sale' unless we specified a different action in the 'SetExpressCheckout' method (possible values include 'Authorization' and 'Capture')</li>
<li>PAYERID - Unique PayPal account identification. This is returned in the URL query parameters as well, in the 'PayerID' parameter, and can also be retrieved from the details returned by 'GetExpressCheckoutDetails'.</li>
</ul>
<pre class="php"><span style="color: #b1b100;">if</span><span style="color: #66cc66;">&#40;</span> <a href="http://www.php.net/isset"><span style="color: #000066;">isset</span></a><span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">$_GET</span><span style="color: #66cc66;">&#91;</span><span style="color: #ff0000;">'token'</span><span style="color: #66cc66;">&#93;</span><span style="color: #66cc66;">&#41;</span> &amp;&amp; !<a href="http://www.php.net/empty"><span style="color: #000066;">empty</span></a><span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">$_GET</span><span style="color: #66cc66;">&#91;</span><span style="color: #ff0000;">'token'</span><span style="color: #66cc66;">&#93;</span><span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#123;</span> <span style="color: #808080; font-style: italic;">// Token parameter exists</span>
	<span style="color: #808080; font-style: italic;">// Get checkout details, including buyer information.</span>
	<span style="color: #808080; font-style: italic;">// We can save it for future reference or cross check with the data we have</span>
	<span style="color: #0000ff;">$checkoutDetails</span> = paypalApiRequest<span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">'GetExpressCheckoutDetails'</span>, <a href="http://www.php.net/array"><span style="color: #000066;">array</span></a><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">'TOKEN'</span> =&gt; <span style="color: #0000ff;">$_GET</span><span style="color: #66cc66;">&#91;</span><span style="color: #ff0000;">'token'</span><span style="color: #66cc66;">&#93;</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span>;
&nbsp;
	<span style="color: #808080; font-style: italic;">// Complete the checkout transaction</span>
	<span style="color: #0000ff;">$requestParams</span> = <a href="http://www.php.net/array"><span style="color: #000066;">array</span></a><span style="color: #66cc66;">&#40;</span>
		<span style="color: #ff0000;">'PAYMENTREQUEST_0_PAYMENTACTION'</span> =&gt; <span style="color: #ff0000;">'Sale'</span>,
		<span style="color: #ff0000;">'PAYERID'</span> =&gt; <span style="color: #0000ff;">$_GET</span><span style="color: #66cc66;">&#91;</span><span style="color: #ff0000;">'PayerID'</span><span style="color: #66cc66;">&#93;</span>
	<span style="color: #66cc66;">&#41;</span>;
&nbsp;
	<span style="color: #0000ff;">$response</span> = paypalApiRequest<span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">'DoExpressCheckoutPayment'</span>,<span style="color: #0000ff;">$requestParams</span><span style="color: #66cc66;">&#41;</span>;
	<span style="color: #b1b100;">if</span><span style="color: #66cc66;">&#40;</span> <a href="http://www.php.net/is_array"><span style="color: #000066;">is_array</span></a><span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">$response</span><span style="color: #66cc66;">&#41;</span> &amp;&amp; <span style="color: #0000ff;">$response</span><span style="color: #66cc66;">&#91;</span><span style="color: #ff0000;">'ACK'</span><span style="color: #66cc66;">&#93;</span> == <span style="color: #ff0000;">'Success'</span><span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#123;</span> <span style="color: #808080; font-style: italic;">// Payment successful</span>
		<span style="color: #808080; font-style: italic;">// We'll fetch the transaction ID for internal bookkeeping</span>
		<span style="color: #0000ff;">$transactionId</span> = <span style="color: #0000ff;">$response</span><span style="color: #66cc66;">&#91;</span><span style="color: #ff0000;">'PAYMENTINFO_0_TRANSACTIONID'</span><span style="color: #66cc66;">&#93;</span>;
	<span style="color: #66cc66;">&#125;</span>
<span style="color: #66cc66;">&#125;</span></pre>
<h2>Just the tip of the iceberg</h2>
<p>In this post I showed a very simple way of working with the PayPal API. I didn't discuss error handling, more complex methods such as DirectPayment (for direct credit-card payments) and more. There is enough to do there that I could fill 10 more articles like this and not cover everything.</p>
<p>If you don't want to deal with the API yourself, you can check out my <a href="http://www.binpress.com/app/simple-php-paypal-payments/20">PayPal class over at Binpress</a>. It covers additional APIs including handling Direct Payments (credit-card payments), Mass payments (paying multiple recipients at the same time) and IPN (instant payment notifications).</p>
 <img src="http://www.techfounder.net/wp-content/plugins/feed-statistics.php?view=1&post_id=654" width="1" height="1" style="display: none;" />
	<div style="">
		<a href="http://twitter.com/share" class="twitter-share-button" data-count="vertical" data-text="Breaking down the PayPal API" data-url="http://www.techfounder.net/2011/04/23/breaking-down-the-paypal-api/"  data-via="erangalperin">Tweet</a>
	</div>
	<script type="text/javascript" src="http://platform.twitter.com/widgets.js"></script>]]></content:encoded>
			<wfw:commentRss>http://www.techfounder.net/2011/04/23/breaking-down-the-paypal-api/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Database optimization techniques you can actually use</title>
		<link>http://www.techfounder.net/2011/03/25/database-profiling-and-optimizing-your-database-the-generic-version/</link>
		<comments>http://www.techfounder.net/2011/03/25/database-profiling-and-optimizing-your-database-the-generic-version/#comments</comments>
		<pubDate>Fri, 25 Mar 2011 01:20:35 +0000</pubDate>
		<dc:creator>Eran Galperin</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Web development]]></category>
		<category><![CDATA[optimization]]></category>
		<category><![CDATA[profiling]]></category>

		<guid isPermaLink="false">http://www.techfounder.net/?p=626</guid>
		<description><![CDATA[I just saw an article on Smashing Magazine titled "Speeding up your website's database". I love Smashing's contribution to the webdev community, but their articles are getting longer and more basic at the same time. I understand the need for simplicity because of the wide audience of Smashing Magazine, but I'd wish they'd give something [...]]]></description>
			<content:encoded><![CDATA[<p>I just saw an article on Smashing Magazine titled "<a target="blank" rel="nofollow" href="http://www.smashingmagazine.com/2011/03/23/speeding-up-your-websites-database/">Speeding up your website's database</a>". I love Smashing's contribution to the webdev community, but their articles are getting longer and more basic at the same time. </p>
<p>I understand the need for simplicity because of the wide audience of Smashing Magazine, but I'd wish they'd give something more than the absolute basics you could find in almost any other site out there. I also didn't like some of the methods mentioned there for profiling (or the code itself), so I here is my starter guide to optimizing database performance.<br />
<span id="more-626"></span></p>
<h3>When do we optimize the database?</h3>
<p>As is noted in the article, page load speed is important. It affects the user-experience as well as Google pagerank when it gets too slow. There are so many variables to account for when trying to improve page load, including page download weight (including all the various assets such as images, javascript and CSS), network latency, browser cache and server headers, server load (requests per second and memory and CPU usage) among others. Yahoo has a <a target="blank" rel="nofollow" href="http://developer.yahoo.com/performance/rules.html">very nice guide</a> for client-side performance tips. We're going to suspect the database as the culprit for the purposes of this article, but you should first observe the complete picture before deciding on what to optimize.</p>
<p>Aside from page load speed, a busy database can affect the rest of the server as well, meaning parts that don't use the database or have very fast running queries could start to slow down.</p>
<h3>Profile first, optimize last</h3>
<p>The basic rule of optimization is to never assume - always verify, using actual data. The process of collecting performance metrics and determining performance issues is called <a target="blank" rel="nofollow"  href="http://en.wikipedia.org/wiki/Profiling_%28computer_programming%29">profiling</a>. We want to know whether database performance is responsible for a significant part of our page load time.</p>
<p>Referring again to the smashing magazine article, the author suggests a profiling method that is basically correct however the implementation leaves a lot to be desired. We won't go into why using globals and outputting inside functions is not good practice, and the author even mentions that this could seriously mess up the layout of the site or the sessions and yet makes no attempt to give out a better solution.</p>
<p>We want to time how much queries are taking to run. There are plenty of timing solutions out in the open - such as <a target="blank" rel="nofollow" href="http://pear.php.net/package/Benchmark/docs/latest/li_Benchmark.html">PEAR_Benchmark</a>, that there is simply no need to build your own unless you want the exercise. The concept is simple - store microtime() values before and after the query for later observation, and the difference would be the timing of the query with good accuracy.</p>
<p>If you are using a database abstraction class (and you should), incorporating a timer to profile every query should be a piece of cake - so no need to hunt down every query and modify the code around it as suggested in the SM article. Wrap the query method of your abstraction class with the timer and use the queries as the keys in the timing array. We used the Zend Framework for all of our previous projects and for our current <a href="http://www.binpress.com">startup</a>, and it comes with a built-in support for profiling which makes it a breeze to get started.</p>
<p>Example code using <a target="blank" rel="nofollow" href="http://framework.zend.com/manual/en/zend.db.profiler.html">Zend_Db_Profiler</a></p>
<pre class="php"><span style="color: #0000ff;">$db</span> = Zend_Db::<span style="color: #006600;">factory</span><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">'PDO_MYSQL'</span>, <span style="color: #0000ff;">$config</span><span style="color: #66cc66;">&#41;</span>; <span style="color: #808080; font-style: italic;">//Set up the database object</span>
 <span style="color: #0000ff;">$db</span> -&gt; <span style="color: #006600;">getProfiler</span><span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span>-&gt;<span style="color: #006600;">setEnabled</span><span style="color: #66cc66;">&#40;</span><span style="color: #000000; font-weight: bold;">true</span><span style="color: #66cc66;">&#41;</span>; <span style="color: #808080; font-style: italic;">// turn on profiler</span>
&nbsp;
<span style="color: #808080; font-style: italic;">//Queries are performed on the page</span>
<span style="color: #808080; font-style: italic;">//...</span>
&nbsp;
<span style="color: #808080; font-style: italic;">// Where we want to show the results</span>
<span style="color: #0000ff;">$profiles</span> = <span style="color: #0000ff;">$profiler</span> -&gt; <span style="color: #006600;">getQueryProfiles</span><span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span>; <span style="color: #808080; font-style: italic;">//An array of all the query profiling</span></pre>
<p>If you are using Firebug (which you should if you are running Firefox) you can install the FirePHP plugin for completely unobtrusive output. The Zend Framework comes with a FirePHP profiler that can send query results directly into your Firebug console.</p>
<pre class="php"><span style="color: #0000ff;">$profiler</span> = <span style="color: #000000; font-weight: bold;">new</span> Zend_Db_Profiler_Firebug<span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">'All DB Queries'</span><span style="color: #66cc66;">&#41;</span>;
<span style="color: #0000ff;">$profiler</span> -&gt; <span style="color: #006600;">setEnabled</span><span style="color: #66cc66;">&#40;</span><span style="color: #000000; font-weight: bold;">true</span><span style="color: #66cc66;">&#41;</span>;
<span style="color: #0000ff;">$db</span> -&gt; <span style="color: #006600;">setProfiler</span><span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">$profiler</span><span style="color: #66cc66;">&#41;</span>;
&nbsp;
<span style="color: #808080; font-style: italic;">//Queries are performed on the page</span>
<span style="color: #808080; font-style: italic;">//...</span>
<span style="color: #808080; font-style: italic;">// No need to output, query profiles will appear in your Firebug console</span></pre>
<p>Pretty convenient. We can go over page by page without disturbing content or messing up sessions and even run this in a production environment, provided we load the profiler only for specific users. The output looks something like this:</p>
<p><img title="firePHP results" src="http://www.techfounder.net/wp-content/uploads/2011/03/firephp.png" alt="" /></p>
<p><b>It's very important to profile using a relevant dataset</b>. If you profile on a development machine that has very different data (and probably much smaller tables) than your production machine, you will get very different results. You should create a test machine that resembles your live dataset as much as possible to get relevant data (as I've shown in an <a href="http://www.techfounder.net/2008/10/12/profiling-queries-with-zend_db-and-optimizing-them-by-hand/">old article about profiling</a>).</p>
<p>Another important note is to avoid looking at cached results. MySQL will cache certain queries - so verify the results of your profiling by running the queries while avoiding caches using <a target="blank" rel="nofollow" href="http://dev.mysql.com/doc/refman/5.5/en/query-cache-in-select.html">SQL_NO_CACHE</a> and <a target="blank" rel="nofollow" href="http://www.mysqlperformanceblog.com/2007/09/12/query-profiling-with-mysql-bypassing-caches/">other means</a>. Compare the first run with subsequent runs of the same query to be sure you are seeing non-cached results.</p>
<p>Aside from profiling the queries in real time, we can also profile queries that are used by daemons and cron jobs and log the results to a file. MySQL has a <a target="blank" rel="nofollow" href="http://dev.mysql.com/doc/refman/5.1/en/slow-query-log.html">built in feature in MySQL</a> that can log slow queries for us while the database daemon is running. As of MySQL 5.1.21 we can get microsecond timing on queries (previously only one-second jumps were supported) so we can get very good measurements with the slow-query log. </p>
<p>The slow query log should be used for monitoring and be checked periodically for possible problems. The rate at which the log fills out also gives an indication of how much your database is slowing down over time and how much time you have left before you need to optimize.</p>
<h2>Optimizing performance</h2>
<p>Suppose we found out some problematic queries on slow pages. There are 4 basic ways to optimize query performance:</p>
<ul>
<li>Rewrite the queries</li>
<li>Change indexing strategy</li>
<li>Change schema</li>
<li>Use an external cache</li>
</ul>
<h3>Examining query execution plans (EXPLAIN)</h3>
<p>Before trying to optimize a slow query, we need to understand what makes it slow. For this purpose MySQL has a query examination tool called <a target="blank" rel="nofollow" href="http://dev.mysql.com/doc/refman/5.5/en/explain.html">EXPLAIN</a>. Add the reserved word 'EXPLAIN' at the beginning of your query to get the execution plan for the query. The execution plan literally 'explains' to us what the database is doing to optimize the query. The MySQL manual has <a target="blank" rel="nofollow" href="http://dev.mysql.com/doc/refman/5.0/en/explain-output.html">a full reference guide</a> to the different values that appear in the plan, and you can see a full walkthrough of using EXPLAIN to optimize a query in <a target="blank" rel="nofollow" href="http://www.slideshare.net/phpcodemonkey/mysql-explain-explained">this slideshow on slideshare</a> (as well as in the profiling article I linked to earlier).</p>
<p>This is a very useful tool, but like all other tools it should be used while <a target="blank" rel="nofollow" href="http://www.mysqlperformanceblog.com/2006/07/24/mysql-explain-limits-and-errors/">being aware of its limitations</a>.</p>
<h3>Common optimizations</h3>
<p><b>1. Looping queries</b></p>
<p>The most basic performance issues often will not be the fault of the database itself. One of the most common mistakes is to query in a loop without need. Most likely looped SELECT queries can be rewritten as a JOIN -</p>
<pre class="php">&nbsp;
<span style="color: #0000ff;">$query</span> = <span style="color: #ff0000;">'SELECT id,name FROM categories'</span>;
<span style="color: #0000ff;">$rows</span> = <span style="color: #0000ff;">$db</span> -&gt; <span style="color: #006600;">fetchAll</span><span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">$query</span><span style="color: #66cc66;">&#41;</span>;
<span style="color: #b1b100;">foreach</span><span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">$rows</span> <span style="color: #b1b100;">as</span> <span style="color: #0000ff;">$row</span><span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#123;</span>
     <span style="color: #0000ff;">$query</span> = <span style="color: #ff0000;">'SELECT id,name FROM sub_categories WHERE category_id='</span> . <span style="color: #66cc66;">&#40;</span>int<span style="color: #66cc66;">&#41;</span> <span style="color: #0000ff;">$row</span><span style="color: #66cc66;">&#91;</span><span style="color: #ff0000;">'id'</span><span style="color: #66cc66;">&#93;</span>;
     <span style="color: #0000ff;">$subCategories</span> = <span style="color: #0000ff;">$db</span> -&gt; <span style="color: #006600;">fetchAll</span><span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">$query</span><span style="color: #66cc66;">&#41;</span>;
     <span style="color: #808080; font-style: italic;">//...</span>
<span style="color: #66cc66;">&#125;</span></pre>
<p>(I'm using Zend Framework syntax since we already assumed we are using a database abstraction class)</p>
<p>This could be rewritten as a join -</p>
<pre class="php">&nbsp;
<span style="color: #0000ff;">$query</span> = <span style="color: #ff0000;">'SELECT categories.id,categories.name,
sub_categories.id AS subcat_id,sub_categories.name AS subcat_name
FROM categories
LEFT JOIN sub_categories ON categories.id=sub_categories.category_id'</span>;
<span style="color: #0000ff;">$rows</span> = <span style="color: #0000ff;">$db</span> -&gt; <span style="color: #006600;">fetchAll</span><span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">$query</span><span style="color: #66cc66;">&#41;</span>;
<span style="color: #b1b100;">foreach</span><span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">$rows</span> <span style="color: #b1b100;">as</span> <span style="color: #0000ff;">$row</span><span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#123;</span>
   <span style="color: #808080; font-style: italic;">// Require a bit of additional logic to format the results, but we have one query instead of many</span>
<span style="color: #66cc66;">&#125;</span></pre>
<p>Inserting and updating rows in a loop can have major overhead as well, and those queries are generally slower than simple SELECT queries (since indexes often need to be updated) and they affect the performance of other queries since they use table / row locks while the data is written (this differs <a target="blank" rel="nofollow" href="http://dev.mysql.com/doc/refman/5.5/en/internal-locking.html">depending on the table engine</a>). I wrote an article almost two years ago on <a href="http://www.techfounder.net/2009/05/14/multiple-row-operations-in-mysql-php/" target="blank" rel="nofollow">multiple row operations</a> that covers how to rewrite looped INSERT / UPDATE queries and includes some benchmarks to show how it improves performance.</p>
<p><b>2. Picking only needed columns</b></p>
<p>It is common to see a wildcard used to pick all columns ('SELECT * FROM ... ') - this however, is not efficient. Depending on the number of participating columns and their type (especially large types such as the TEXT variants), we could be selecting much more data from the database than we actually need. The query will take longer to return since it needs to transfer more data (from the hard-disk if it doesn't hit the cache) and it will take up more memory doing so.</p>
<p>Picking only the needed columns is a good general practice to use, and avoids those problems.</p>
<p><b>3. Filtering rows correctly and using indexes</b></p>
<p>Our main goal is to select the smallest amount of rows we need and doing so in the fastest way possible. We want to filter rows using indexes, and in general we want to avoid full table scans unless it is absolutely needed (aside from edge cases where <a target="blank" rel="nofollow" href="http://www.mysqlperformanceblog.com/2006/06/02/indexes-in-mysql/">it actually improves performance</a>). The MySQL manual <a target="blank" rel="nofollow" href="http://dev.mysql.com/doc/refman/5.5/en/where-optimizations.html">has some great information</a> on optimizing the WHERE clause, and I'll dive into a bit more detail - </p>
<p>Filtering conditions include the WHERE, ON (for joins) and HAVING clauses. As much as possible, <a target="blank" rel="nofollow" href="http://dev.mysql.com/doc/refman/5.5/en/how-to-avoid-table-scan.html">we want those clauses to hit indexes</a> - unless we are selecting a very large amount of rows, index lookup is much faster than a full table scan. Those clauses should be used along with the LIMIT clause if relevant to filter the amount of rows / data returned by the query. The LIMIT clause itself can lend some important optimizations for queries <a target="blank" rel="nofollow" href="http://dev.mysql.com/doc/refman/5.5/en/limit-optimization.html">if used correctly</a>.</p>
<p>Since our goal is to hit indexes with our WHERE clause, an important rule is to avoid using calculations there. When the filtering condition has to be calculated for each row, the WHERE clause cannot use an index.</p>
<p>Example - fetching users created in the last 4 weeks:</p>
<pre class="sql"><span style="color: #993333; font-weight: bold;">SELECT</span> id,name <span style="color: #993333; font-weight: bold;">FROM</span> users <span style="color: #993333; font-weight: bold;">WHERE</span> created - NOW<span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span> &lt; INTERVAL <span style="color: #cc66cc;">4</span> WEEK</pre>
<p>Since the value of the `created` column changes from row to row, we now have a calculation in the left-hand side of the condition. This could be rewritten so that the calculation doesn't change and thus will only be performed once:</p>
<pre class="sql"><span style="color: #993333; font-weight: bold;">SELECT</span> id,name <span style="color: #993333; font-weight: bold;">FROM</span> users <span style="color: #993333; font-weight: bold;">WHERE</span> created &gt; NOW<span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span> - INTERVAL <span style="color: #cc66cc;">4</span> WEEK</pre>
<p>This query can use an index on the `created` column and should perform much better.</p>
<p>Another less common calculation but a very problematic one is <a target="blank" rel="nofollow" href="http://en.wikipedia.org/wiki/Correlated_subquery">correlated subqueries</a> in the WHERE clause.</p>
<p>Selecting the lowest priced fruit from several fruit types:</p>
<pre class="sql"><span style="color: #993333; font-weight: bold;">SELECT</span> type, variety, price
<span style="color: #993333; font-weight: bold;">FROM</span> fruits
<span style="color: #993333; font-weight: bold;">WHERE</span> price = <span style="color: #66cc66;">&#40;</span>
    <span style="color: #993333; font-weight: bold;">SELECT</span> MIN<span style="color: #66cc66;">&#40;</span>price<span style="color: #66cc66;">&#41;</span> <span style="color: #993333; font-weight: bold;">FROM</span> fruits <span style="color: #993333; font-weight: bold;">AS</span> f <span style="color: #993333; font-weight: bold;">WHERE</span> f.type = fruits.type
<span style="color: #66cc66;">&#41;</span></pre>
<p>(Example taken from the <a target="blank" rel="nofollow" href="http://www.xaprb.com/blog/2006/12/07/how-to-select-the-firstleastmax-row-per-group-in-sql/">excellent Xaprb article</a> on the topic - you should read it). This query could be rewritten as JOIN, moving the subquery from the WHERE clause - </p>
<pre class="sql"><span style="color: #993333; font-weight: bold;">SELECT</span> f.type, f.variety, f.price
<span style="color: #993333; font-weight: bold;">FROM</span> <span style="color: #66cc66;">&#40;</span>
   <span style="color: #993333; font-weight: bold;">SELECT</span> type, MIN<span style="color: #66cc66;">&#40;</span>price<span style="color: #66cc66;">&#41;</span> <span style="color: #993333; font-weight: bold;">AS</span> minprice
   <span style="color: #993333; font-weight: bold;">FROM</span> fruits
   <span style="color: #993333; font-weight: bold;">GROUP</span> <span style="color: #993333; font-weight: bold;">BY</span> type
<span style="color: #66cc66;">&#41;</span> <span style="color: #993333; font-weight: bold;">AS</span> minfruits
<span style="color: #993333; font-weight: bold;">INNER</span> <span style="color: #993333; font-weight: bold;">JOIN</span> fruits <span style="color: #993333; font-weight: bold;">AS</span> f <span style="color: #993333; font-weight: bold;">ON</span> f.type = minfruits.type <span style="color: #993333; font-weight: bold;">AND</span> f.price = minfruits.minprice</pre>
<p>Ideally, MySQL would've optimized both of those the same, but since it is usually not the case, rewriting correlated subqueries as joins is preferred.</p>
<p><b>4. Indexing correctly</b></p>
<p>Whether optimizations are needed or not is dependent on the EXPLAIN results we mentioned previously. If the execution plan indicates an index is not being used or a non-selective index has been picked, we need to understand why and change our indexing strategy accordingly (or use <a target="_blank" rel="nofollow" href="http://dev.mysql.com/doc/refman/5.5/en/index-hints.html">index hints</a>). </p>
<p>MySQL can use one index per table alias in a query, so we need to plan our indexes to maximize their effectiveness. Using more indexes than is necessary can have adverse affects - as it slows down the operation of INSERT and UPDATE queries, while taking up more memory. Some indexes can even <a target="_blank" rel="nofollow" href="http://www.mysqlperformanceblog.com/2007/08/28/do-you-always-need-index-on-where-column/">slow down performance</a> depending on their selectivity.</p>
<p>If we use ordering clauses such as ORDER and GROUP BY, our indexes should often be composite indexes (indexes covering more than one column) to allow for both the filtering and ordering to use an index.</p>
<p><b>5. Picking the right engine for your data</b></p>
<p>MySQL has <a target="_blank" rel="nofollow" href="http://dev.mysql.com/doc/refman/5.5/en/storage-engines.html">a pluggable engine design</a>, which allows you to use different engine types to store your data, each with its own advantages and drawbacks. The two main engines are MyISAM and InnoDB, and the differences between them affect much more than just performance - InnoDB is an ACID compliant transactional engine, while MyISAM sacrifices some integrity and consistancy features for simplicity and performance. Having said that, <a href="http://www.mysqlperformanceblog.com/2006/05/29/join-performance-of-myisam-and-innodb/">MyISAM is not necessarily faster</a>, only in some cases.</p>
<p>I use InnoDB for all of my table unless I need a full-text index (a MyISAM only feature), since my tables usually have a lot of write activity. InnoDB uses row-level locks which really helps performance for such use, while MyISAM uses table locks for write operations. It also optimizes write operations by <a rel="nofollow" target="_blank" href="http://www.xaprb.com/blog/2006/07/04/how-to-exploit-mysql-index-optimizations/">treating indexes differently</a> than MyISAM - InnoDB uses <a target="_blank" rel="nofollow" href="http://www.xaprb.com/blog/2006/05/10/when-to-avoid-and-when-to-use-surrogate-keys-in-innodb-tables/">clustered indexes</a>, so picking the right primary key is critical.</p>
<h3>Caching</h3>
<p>In the case that our optimizations does not yield sufficient performance benefits (either due to technical reasons or our own skill level), <a target="blank" rel="nofollow" href="http://en.wikipedia.org/wiki/Cache">caching</a> is a viable strategy to reduce database load. You should always try and optimize the database itself first, since caching will add another of complexity to our application.</p>
<p>MySQL has an internal query cache that caches results from frequently running queries if it <a target="blank" rel="nofollow" href="http://dev.mysql.com/doc/refman/5.1/en/query-cache-operation.html">meets certain requirements</a>. If our queries are cached by MySQL (this can be verified by running the queries several times), there is no need to cache it - we just need to be aware that a MySQL service restart could cause a noticeable slow down while the cache is being primed again. You can read more about the <a target="blank" rel="nofollow" href="http://www.mysqlperformanceblog.com/2010/09/23/more-on-dangers-of-the-caches/">dangers of the internal cache</a> on the excellent MySQL performance blog (a must read for any serious MySQL user).</p>
<p>There are many caching strategies and that is the topic for another post. Common options include caching to disk (files) or caching to memory (using solutions such as <a target="blank" rel="nofollow" href="http://www.mysqlperformanceblog.com/2006/09/27/apc-or-memcached/">memcache or APC</a>). Another form of caching is to the database - by de-normalizing the schema to store data that is the result of expensive to run queries.</p>
<h3>Server tuning and beyond</h3>
<p>Everything covered here is just the tip of the iceberg - it gets rapidly more advanced as you get dig deeper, including tuning MySQL server variables (and you should - <a target="blank" rel="nofollow" href="http://www.mysqlperformanceblog.com/2007/11/01/innodb-performance-optimization-basics/">at least the basics</a>), the server itself (hardware / software) and using related tools such as <a href="http://sphinxsearch.com/">sphinx</a> and <a href="http://lucene.apache.org/java/docs/index.html">lucene</a> to offload some of the work. I tried to give a good starting point and as many references as possible for getting a good start to getting your database in shape. </p>
<p>I linked to several excellent resources in this article, such as the <a href="http://dev.mysql.com/doc/refman/5.5/en/">MySQL manual</a>, <a href="http://www.mysqlperformanceblog.com/">MySQL performance blog</a> and <a href="http://www.xaprb.com/blog/">Xaprb</a> (the last two are of <a href="http://www.percona.com/">Percona</a> fame - world-class experts on MySQL). I suggest you start visiting those regularly as they offer excellent advice.</p>
 <img src="http://www.techfounder.net/wp-content/plugins/feed-statistics.php?view=1&post_id=626" width="1" height="1" style="display: none;" />
	<div style="">
		<a href="http://twitter.com/share" class="twitter-share-button" data-count="vertical" data-text="Database optimization techniques you can actually use" data-url="http://www.techfounder.net/2011/03/25/database-profiling-and-optimizing-your-database-the-generic-version/"  data-via="erangalperin">Tweet</a>
	</div>
	<script type="text/javascript" src="http://platform.twitter.com/widgets.js"></script>]]></content:encoded>
			<wfw:commentRss>http://www.techfounder.net/2011/03/25/database-profiling-and-optimizing-your-database-the-generic-version/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>(mis-)Adventures with Amazon Simple Email Services (SES)</title>
		<link>http://www.techfounder.net/2011/03/23/mis-adventures-with-amazon-simple-email-services-ses/</link>
		<comments>http://www.techfounder.net/2011/03/23/mis-adventures-with-amazon-simple-email-services-ses/#comments</comments>
		<pubDate>Tue, 22 Mar 2011 23:49:01 +0000</pubDate>
		<dc:creator>Eran Galperin</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Web development]]></category>
		<category><![CDATA[Zend Framework]]></category>
		<category><![CDATA[amazon ses]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[sockets]]></category>

		<guid isPermaLink="false">http://www.techfounder.net/?p=611</guid>
		<description><![CDATA[Amazon has recently launched a new service as part of its web-services offerings - Amazon Simple Email Service (or SES for short), an HTTP API for sending Emails. The main selling points are Amazon's usual scalability power, as well as a relatively low price point for sending Emails (10 cents per 1000 Emails, not counting [...]]]></description>
			<content:encoded><![CDATA[<p>Amazon has recently launched a new service as part of its web-services offerings - <a rel="nofollow" href="http://aws.amazon.com/ses/" target="_blank">Amazon Simple Email Service</a> (or SES for short), an HTTP API for sending Emails. The main selling points are Amazon's usual scalability power, as well as a relatively low price point for sending Emails (10 cents per 1000 Emails, not counting bandwidth). They also promise to improve deliverability using filters and feedback loops.<br />
<span id="more-611"></span><br />
Having used <a rel="nofollow" href="http://sendgrid.com/" target="_blank">Sendgrid</a> for a while at our startup, <a href="http://www.binpress.com">Binpress</a>, we were overall satisfied with their service. We did have some problems with seemingly innocent Emails ending up in spam boxes, and combined with the better pricing we thought Amazon SES deserved a shot.</p>
<h3>This ain't no rocket science</h3>
<p>Amazon's claims of simplicity would be justified indeed if you compare it with building a complete Email sending infrastructure with similar scaling power as their own offering. However, compared to integrating competitors such as Sendgrid or any SMTP based service, SES integration is anything but simple.</p>
<p>Using any SMTP abstraction class, integrating an SMTP service involves passing the credentials and service endpoint IP. That's it. We use the Zend_Mail component from the Zend Framework and our code looked like this:</p>
<pre class="php"><span style="color: #0000ff;">$config</span> = <a href="http://www.php.net/array"><span style="color: #000066;">array</span></a><span style="color: #66cc66;">&#40;</span>
	<span style="color: #ff0000;">'auth'</span> =&amp;gt; <span style="color: #ff0000;">'login'</span>,
	<span style="color: #ff0000;">'username'</span> =&amp;gt; <span style="color: #ff0000;">'user@domain.com'</span>,
	<span style="color: #ff0000;">'password'</span> =&amp;gt; <span style="color: #ff0000;">'ourpassword'</span>
<span style="color: #66cc66;">&#41;</span>;
<span style="color: #0000ff;">$transport</span> = <span style="color: #000000; font-weight: bold;">new</span> Zend_Mail_Transport_Smtp<span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">'smtp.sendgrid.net'</span>, <span style="color: #0000ff;">$config</span><span style="color: #66cc66;">&#41;</span>;
&nbsp;
<span style="color: #0000ff;">$mail</span> = <span style="color: #000000; font-weight: bold;">new</span> Zend_Mail<span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span>;
<span style="color: #808080; font-style: italic;">// Prepare mail</span>
<span style="color: #0000ff;">$mail</span> -&amp;gt; send<span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">$transport</span><span style="color: #66cc66;">&#41;</span>;</pre>
<p>Pretty straightforward. Getting Amazon SES up and running was anything but. I ended up writing a nice SES abstraction and even a Zend_Mail transport to go with it (you can <a href="http://www.binpress.com/app/amazon-ses-wrapper-and-cp/192">check out the code here</a> if you are not interested in the details).</p>
<h3>Requests and authentication</h3>
<p>The first hurdle to overcome is using Amazon's authentication scheme. Unlike most HTTP APIs, Amazon doesn't pass a request signature as a request parameter. Instead, it uses a custom HTTP header called 'X-Amzn-Authorization' - the documentation of which is hidden in <a rel="nofollow" href="http://docs.amazonwebservices.com/ses/latest/DeveloperGuide/HMACShaSignatures.html" target="_blank">one of the inner pages</a> of the docs as a seemingly meaningless footnote. You cannot do anything without authorizing first! this section should've been front and center and explained much more clearly.</p>
<p>Since we need custom headers, that immediately rules out something simple like file_get_contents() (which probably should be avoided anyway for it's simplistic error handling). My first thought was to use cURL, like I do with most HTTP APIs - however after struggling with it for around an hour, I came to realize that cURL (at least for PHP) doesn't allow you to specify custom headers that are not a part of the HTTP standard - like Amazon's authentication header.</p>
<p>I briefly considered using <a rel="nofollow" href="http://php.net/manual/en/function.http-request.php" target="_blank">http_request()</a>, however that requires installing a PECL extension that is not bundled with the default installation, so that was scraped. The only viable option left for me was to construct the HTTP request myself from scratch and negotiate with the SES API using socket functions. Simple, right?</p>
<p>Luckily, SES docs do provide <a rel="nofollow" href="http://docs.amazonwebservices.com/ses/latest/DeveloperGuide/QueryRequestAuthentication.Examples.html" target="_blank">example requests</a>. Being this was the first time in a while for me to experiment with raw HTTP requests and socket connections, it took me some time to get my bearings.</p>
<h3>Socked up and raring to go</h3>
<p>So was I ready to rock my Email world with Amazon SES at this point? not quite. Amazon's example POST request looks like this -</p>
<pre>POST / HTTP/1.1
Host: email.us-east-1.amazonaws.com
Content-Type: application/x-www-form-urlencoded
Date: Tue, 25 May 2010 21:20:27 +0000
X-Amzn-Authorization: AWS3-HTTPS AWSAccessKeyId=AKIADQKE4EXAMPLE,Algorithm=HMACSHA256,Signature=lBP67vCvGl ...</pre>
<p>The sample request was failing with a cryptic "Unable to determine service/operation name to be authorized" error. Can you tell what's missing? it's the Content-Length header. Sure enough, adding it finally made my first request to go through to the API. More time wasted because of a bad usage example in the docs.</p>
<h3>Obviously, there's more</h3>
<p>We've gone this far without mentioning one of the basic annoyances inherent to Amazon SES. You must verify <a rel="nofollow" href="http://docs.amazonwebservices.com/ses/latest/GettingStartedGuide/VerifyEmailAddress.html" target="_blank"><em><strong>each and every Email address</strong></em></a> you want to send to and from before you can actually do that. I'm sorry, come again? yes, in order to help protect itself from spammers, Amazon implemented yet another barrier to adoption. Here, user experience means something a user experiences as he contemplates smashing his screen and not a craft used to improve costumer satisfaction.</p>
<p>I mean, I understand the underlying reasoning, but this is going a bit far. Fortunately, requesting "<a rel="nofollow" href="http://aws.amazon.com/ses/fullaccessrequest/" target="_blank">production access</a>" can alleviate verifying recipient addresses leaving the restriction only on source (from) addresses. Seeing that the service is basically useless without it, we sent out a request and put this integration project aside until we got it (which took about a week). It's a good thing they're cheap and scalable (at least, by reputation), cause simple it hasn't been so far.</p>
<h3>Finally, up and running</h3>
<p>After we got our production access and verified some of our Emails, we could finally put Amazon SES to use. The code provided in their SDK for PHP is simply terrible, and it doesn't even work for SES (<strong>UPDATE:</strong> while this was true when I tested it a few weeks ago, one of the commentators has clued me in to that it has been updated and it does work as of now), so I've completed my own API wrapper and even wrote a transport for Zend_Mail that we could plug instead of our previous SMTP transport (shown at the beginning of the post).</p>
<p>I even added a simple control panel for listing and editing verified Email addresses as well as viewing usage statistics and quotas. You can <a href="http://www.binpress.com/app/amazon-ses-wrapper-and-cp/192">download it for free</a> for non-commercial use (there are also licenses for commercial applications if you need it).</p>
<p>A couple of days ago we sent out a newsletter to about 350 recipients and in the middle Amazon SES crapped on us by throwing a "sending rate quota exceeded" exception. Since we don't have an internal queue (we were counting on theirs), we basically had no way of knowing which Emails got sent and which didn't.</p>
<p>It's been almost a month since we were granted production access, and according to their <a rel="nofollow" href="http://docs.amazonwebservices.com/ses/latest/DeveloperGuide/ManagingActivity.PlanningAhead.html" target="_blank">quota growth table</a>, our rate should have been 90 Emails per second. However, it was still at the start value of 1 per second, which we exceeded apparently. I would've expected it to go into a queue and be sent slowly instead of throwing an error, but that's just me I guess.</p>
<p>Another issue that has popped up lately, is one of their API actions returning a malformed XML after the information grew beyond a certain size. Since there is no official technical support for non premium users like me, even for reporting bugs relating to their beta service, <a rel="nofollow" href="https://forums.aws.amazon.com/thread.jspa?threadID=62810&amp;tstart=0" target="_blank">I posted the error on their forums</a> and hoped for the best. I have received no response on it so far.</p>
<p>So that's the state of Amazon SES for you. A very cheap and apparently scalable service with some kinks that need to be ironed out. The API is nothing to write home about as well, but with some abstractions that is acceptable.</p>
<p>I hope this article has given you some sense what it takes to integrate Amazon SES and how to do it. If you have any particular questions, don't hesitate to probe me in the comments.</p>
 <img src="http://www.techfounder.net/wp-content/plugins/feed-statistics.php?view=1&post_id=611" width="1" height="1" style="display: none;" />
	<div style="">
		<a href="http://twitter.com/share" class="twitter-share-button" data-count="vertical" data-text="(mis-)Adventures with Amazon Simple Email Services (SES)" data-url="http://www.techfounder.net/2011/03/23/mis-adventures-with-amazon-simple-email-services-ses/"  data-via="erangalperin">Tweet</a>
	</div>
	<script type="text/javascript" src="http://platform.twitter.com/widgets.js"></script>]]></content:encoded>
			<wfw:commentRss>http://www.techfounder.net/2011/03/23/mis-adventures-with-amazon-simple-email-services-ses/feed/</wfw:commentRss>
		<slash:comments>13</slash:comments>
		</item>
		<item>
		<title>Last call for the Binpress programming contest</title>
		<link>http://www.techfounder.net/2011/02/15/last-call-for-binpress-programming-contest/</link>
		<comments>http://www.techfounder.net/2011/02/15/last-call-for-binpress-programming-contest/#comments</comments>
		<pubDate>Mon, 14 Feb 2011 23:54:16 +0000</pubDate>
		<dc:creator>Eran Galperin</dc:creator>
				<category><![CDATA[Binpress]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Web development]]></category>
		<category><![CDATA[asp.net]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://www.techfounder.net/?p=605</guid>
		<description><![CDATA[The Binpress programming contest is entering the last stretch - 10 days left until the end. Over $40,000 in prize value is still up for grabs - request an invitation and get your submission entered. A short recap - my startup, Binpress - a marketplace for source-code, is running a programming contest for best submission [...]]]></description>
			<content:encoded><![CDATA[<p>The <a href="http://www.binpress.com/contest?utm_source=techfounder&amp;utm_medium=referral&amp;utm_campaign=blogs">Binpress programming contest</a> is entering the last stretch - 10 days left until the end. Over $40,000 in prize value is still up for grabs - request an invitation and get your submission entered.</p>
<p>A short recap - my startup, <a href="http://www.binpress.com/?utm_source=techfounder&amp;utm_medium=referral&amp;utm_campaign=blogs">Binpress</a> - a marketplace for source-code, is running a programming contest for best submission of a source-code package in one of the 6 top web languages - PHP, Python, Ruby, Java, ASP.NET (C#, VB.NET) and Javascript. One grand winner and two runner ups will be chosen, as well as the top pick for each language. We have over $40,000 value in cash and prizes that is sponsored by companies such as Google, Amazon, PayPal and Zend.</p>
<p>So far we've had over 70 submissions in the various languages. Some of the submissions are really top notch and there's no clear winner right now. Submissions will start go out to our judges next week for review and the judging will take up to two weeks after the deadline for the contest.</p>
<p>Hope to see what you have to offer there!</p>
 <img src="http://www.techfounder.net/wp-content/plugins/feed-statistics.php?view=1&post_id=605" width="1" height="1" style="display: none;" />
	<div style="">
		<a href="http://twitter.com/share" class="twitter-share-button" data-count="vertical" data-text="Last call for the Binpress programming contest" data-url="http://www.techfounder.net/2011/02/15/last-call-for-binpress-programming-contest/"  data-via="erangalperin">Tweet</a>
	</div>
	<script type="text/javascript" src="http://platform.twitter.com/widgets.js"></script>]]></content:encoded>
			<wfw:commentRss>http://www.techfounder.net/2011/02/15/last-call-for-binpress-programming-contest/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Binpress launched and a programming contest</title>
		<link>http://www.techfounder.net/2011/01/28/binpress-launched-and-a-programming-contest/</link>
		<comments>http://www.techfounder.net/2011/01/28/binpress-launched-and-a-programming-contest/#comments</comments>
		<pubDate>Fri, 28 Jan 2011 00:49:11 +0000</pubDate>
		<dc:creator>Eran Galperin</dc:creator>
				<category><![CDATA[Lionite]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Web development]]></category>
		<category><![CDATA[asp.net]]></category>
		<category><![CDATA[contest]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://www.techfounder.net/?p=593</guid>
		<description><![CDATA[I'm excited to announce that we launched Binpress (I'm a co-founder), a marketplace for source-code where web developers can buy and sell source-code from each other. To kick start our launch, we've organized a programming contest with over $40k in cash and prizes, sponsored by companies such as Google, Amazon, PayPal and Zend. Check out [...]]]></description>
			<content:encoded><![CDATA[<p>I'm excited to announce that we launched <a href="http://binpress.com">Binpress</a> (I'm a co-founder), a marketplace for source-code where web developers can buy and sell source-code from each other.</p>
<p>To kick start our launch, we've organized a programming contest with over $40k in cash and prizes, sponsored by companies such as Google, Amazon, PayPal and Zend. Check out the <a href="http://binpress.com/contest">contest site</a>, and join if you think you have what it takes - the contest will run until 24th of Feb., so get cracking!</p>
 <img src="http://www.techfounder.net/wp-content/plugins/feed-statistics.php?view=1&post_id=593" width="1" height="1" style="display: none;" />
	<div style="">
		<a href="http://twitter.com/share" class="twitter-share-button" data-count="vertical" data-text="Binpress launched and a programming contest" data-url="http://www.techfounder.net/2011/01/28/binpress-launched-and-a-programming-contest/"  data-via="erangalperin">Tweet</a>
	</div>
	<script type="text/javascript" src="http://platform.twitter.com/widgets.js"></script>]]></content:encoded>
			<wfw:commentRss>http://www.techfounder.net/2011/01/28/binpress-launched-and-a-programming-contest/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Zend Framework dependency manager</title>
		<link>http://www.techfounder.net/2010/11/02/zend-framework-dependency-manager/</link>
		<comments>http://www.techfounder.net/2010/11/02/zend-framework-dependency-manager/#comments</comments>
		<pubDate>Tue, 02 Nov 2010 13:38:20 +0000</pubDate>
		<dc:creator>Eran Galperin</dc:creator>
				<category><![CDATA[Open Source]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Zend Framework]]></category>

		<guid isPermaLink="false">http://www.techfounder.net/?p=571</guid>
		<description><![CDATA[Reusing Zend Framework based code can be a bit of a pain - you do not want to include the entire framework just for a few classes, yet tracking down the dependencies needed to run your code can be time consuming and it's hard to tell if you aren't missing anything. To overcome those issues, [...]]]></description>
			<content:encoded><![CDATA[<p>Reusing Zend Framework based code can be a bit of a pain - you do not want to include the entire framework just for a few classes, yet tracking down the dependencies needed to run your code can be time consuming and it's hard to tell if you aren't missing anything.</p>
<p>To overcome those issues, I wrote a utility class to automatically resolve Zend Framework dependencies by fetching them from the online SVN repository when they are needed (and then storing them locally). The class is available for download from <a title="Download Zend Framework dependency manager" href="http://www.binpress.com/app/zf-dependency-manager/30">Binpress</a> under the <a title="MIT open-source license" href="http://www.opensource.org/licenses/mit-license.php">MIT license</a>.</p>
<p><span id="more-571"></span></p>
<p><strong>How does it work:</strong></p>
<ul>
<li>An autoload function is prepended to the spl autoload stack</li>
<li>The auotload function catches requests for Zend Framework classes</li>
<li>If the class is not available locally, the dependency manager downloads it from the SVN repository</li>
<li>If the class has additional dependencies handled by internal class managers in the ZF (such as plugins and helpers), those are fetched as well</li>
<li>All fetched class files are stored locally in a predefined location (such as /library/Zend as is the norm)</li>
<li>The class is then loaded</li>
</ul>
<p><strong>Usage:</strong></p>
<ul>
<li>Include the Zdm class (Zdm.php) at the beginning of the application (in the bootstrap, if you are using one)</li>
<li>Run the static start() function</li>
<li>The start() function accepts several optional parameters
<ul>
<li>'libraryPath' - The local path to store fetched ZF classes. The default is the same directory as the Zdm class file (expecting it to be under /library)</li>
<li>'zfVersion' - The Zend Framework version to fetch. Default is 1.10.8</li>
<li>'prependStack' - By default the autoload function is prepended to the stack. If this interferes with any existing autoload functions, you can set this value to false and it will be appended to the stack instead.</li>
<li>'repository' - Change the location of the repository to fetch ZF classes from. You can set this value to a local installation of the framework for better performance.</li>
</ul>
</li>
<li>The first time the application is run with the dependency manager, it might take some  time to download all the dependencies. To avoid the script timing out, you should set the time limit to '0' using set_time_limit()</li>
</ul>
<p><strong>Notes</strong> - you need to remove 'require' and 'require_once' statements that load Zend Framework classes. Those statements produce a fatal error when the file is not found and cannot be trapped by the autoload function. I tried to recover from the warning sent before the fatal error, however it could not prevent the fatal error from happening.</p>
<p>This class can be used to minimize the needed framework build for current projects as well. Rename your Zend Framework library directory, and allow the dependency manager to build the library from classes that are actually used in the application. This usually results in a much smaller framework footprint. I've successfully used this class in several active projects to recreate the needed framework library that runs the entire application.</p>
<p>It is likely there are some dependencies that are not mapped yet in the class. If you encounter such dependencies, please let me so I could add it to the class.</p>
 <img src="http://www.techfounder.net/wp-content/plugins/feed-statistics.php?view=1&post_id=571" width="1" height="1" style="display: none;" />
	<div style="">
		<a href="http://twitter.com/share" class="twitter-share-button" data-count="vertical" data-text="Zend Framework dependency manager" data-url="http://www.techfounder.net/2010/11/02/zend-framework-dependency-manager/"  data-via="erangalperin">Tweet</a>
	</div>
	<script type="text/javascript" src="http://platform.twitter.com/widgets.js"></script>]]></content:encoded>
			<wfw:commentRss>http://www.techfounder.net/2010/11/02/zend-framework-dependency-manager/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Software licenses for dummies</title>
		<link>http://www.techfounder.net/2010/10/05/software-licenses-for-dummies/</link>
		<comments>http://www.techfounder.net/2010/10/05/software-licenses-for-dummies/#comments</comments>
		<pubDate>Tue, 05 Oct 2010 12:40:39 +0000</pubDate>
		<dc:creator>Eran Galperin</dc:creator>
				<category><![CDATA[Business Development]]></category>
		<category><![CDATA[Lionite]]></category>
		<category><![CDATA[Open Source]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Web development]]></category>

		<guid isPermaLink="false">http://www.techfounder.net/?p=538</guid>
		<description><![CDATA[The legal aspects of selling software are, for the most part, pretty vague for most developers. During preparations for launching our software market, Binpress, I absorbed a lot of information from our very good copyright  attorney and got to understand much better the nuances of software licensing. Here are some of the lessons I learned. [...]]]></description>
			<content:encoded><![CDATA[<p>The legal aspects of selling software are, for the most part, pretty vague for most developers. During preparations for launching our <a title="binpress - web application component store" href="http://binpress.com">software market</a>, Binpress, I absorbed a lot of information from our <a href="http://2jk.org/english/">very good copyright  attorney</a> and got to understand much better the nuances of software licensing. Here are some of the lessons I learned.</p>
<p><span id="more-538"></span></p>
<h2>You need a software license when you are selling software</h2>
<p>Seems obvious, right? however, many freelance developers do not use any license. Common misconception seems to be that when you develop custom code for a client, he basically owns the code when the job is done. <strong>That premise is usually false</strong>. While the license given to custom software solutions is usually liberal, allowing changes to source-code (with conditions) - there are many restrictions that apply on the buyer. For example, it's very rare to allow a client to resell your work as his - though without a license, he can certainly do that.</p>
<p>The purpose of a license is to protect your intellectual property (IP) and copyright. While you may intend to allow a client to modify the source code you provide to fit his needs, it does not necessarily mean he can sell it or include it in his other works.</p>
<p>Lets go over some decisions you have to make when writing a software license and explain the legal terms that describe it:</p>
<h3>Do you want to allow clients to redistribute your software?</h3>
<p>The most basic and critical question. If you answer <strong>YES</strong>, the license is called a <strong>Sublicensable</strong> license and it allows license holders (your clients) to resell your code with their own license, as long as that license does not contradict your original license.</p>
<p>Sometimes, an additional condition is added that the software cannot be sold <em>as is</em> and must be included in a greater work to be sublicensed. This condition prevents your client from becoming your competitor and basically selling the same product you are selling. A sublicesnable license should be used when you want to allow clients to sell solutions based on your work - for example, the client wants to sell CMS product that uses a plug-in developed by you.</p>
<p>The opposite of a sublicesnable license is a <strong>Personal</strong> license. A personal license prohibits license holders from reselling, renting or even allowing use of your code by 3rd parties. This type of license is common when you are selling premade (non-custom) code and want to limit use to the client only. The vast majority of desktop applications are sold under this license.</p>
<h3>Do you want to allow clients to modify the source code?</h3>
<p>Despite what it may seem, a sublicensable license by itself does not allow clients to modify your code. In order to allow that, the license must include <strong>the right to create derivative works</strong>. A derivative work is a modified version of your source code.</p>
<p>Having the right to create derivative works in a sublicensable license by itself does not give the client the right to sell modifications (only the original code). In order to allow the sale of a modified version of your source code (either as standalone or as a part of a larger work), the license must include <strong>the</strong> <strong>right to distribute derivative works</strong>.</p>
<h3>How many sites and servers do you want to allow clients to use  your software on?</h3>
<p>This web-oriented clause restricts the client's use of the software to pre-determined number of sites / servers. While more relevant if you are selling a non-custom solution, it can still apply to custom solutions as well. You can also provide the right to use the software on unlimited sites / servers. A common approach is to offer different pricing (mostly for non-custom solutions) depending on the extent of use granted.</p>
<h3>Do you want to allow commercial use of your software?</h3>
<p>While this condition is true for most custom solutions, there are cases - such as when you are giving a discount to a non-profit - where you want to prohibit commercial use. It is common for off-the-shelf solutions to have a pricing strategy in which the basic product is given for free and commercial versions or extensions are sold for a fee. In order to allow that to happen, commercial use must be covered by the license.</p>
<p>There are basically three main options - royalty free commercial use, non-commercial use and commercial use with conditions. The last one states that commercial use is allowed if certain criteria are met (criteria that you need to decide upon and must be specified in the license).</p>
<h3>Do you want to allow clients to sell their license?</h3>
<p>Self explanatory. In legal terms - a license that allows license holders to sell their license and their right to use the software to a 3rd party, is called an <strong>assignable </strong>license. A license that restricts sale of the license is called a <strong>non-assignable &amp; non-transferable</strong> license.</p>
<h3>Do you want to grant your clients an exclusive license?</h3>
<p>An exclusive license allows you to sell your software to only one client (granting them exclusivity). This could be a requirement by a client, or your way to insure them that you will not be reselling the custom solution they paid you to develop. In most cases this only applies to custom solutions.</p>
<p><strong>This is just a partial list</strong> - there are many more minor restrictions and rights that can be included in a software license. We cover some of those in our <a href="http://www.binpress.com/page/licensing">licensing guide</a> on <a href="http://binpress.com">binpress</a>, and there even more less common clauses that can be applied. If you want to create a legally termed and binding software license using those conditions, we provide a commercial license generator for registered developers in our marketplace.</p>
<p>If you have any questions regarding any of the topics I covered, I would be happy to elaborate or even pass it on to our attorney. The next time you will be talking to clients about a potential project, be sure to have a ready software license that covers their rights and restrictions. This can save you much anguish and provide you with legal protection in the case that things go south.</p>
 <img src="http://www.techfounder.net/wp-content/plugins/feed-statistics.php?view=1&post_id=538" width="1" height="1" style="display: none;" />
	<div style="">
		<a href="http://twitter.com/share" class="twitter-share-button" data-count="vertical" data-text="Software licenses for dummies" data-url="http://www.techfounder.net/2010/10/05/software-licenses-for-dummies/"  data-via="erangalperin">Tweet</a>
	</div>
	<script type="text/javascript" src="http://platform.twitter.com/widgets.js"></script>]]></content:encoded>
			<wfw:commentRss>http://www.techfounder.net/2010/10/05/software-licenses-for-dummies/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>On the pitfalls of date validation with the Zend Framework</title>
		<link>http://www.techfounder.net/2010/06/21/on-the-pitfalls-of-date-validation-with-the-zend-framework/</link>
		<comments>http://www.techfounder.net/2010/06/21/on-the-pitfalls-of-date-validation-with-the-zend-framework/#comments</comments>
		<pubDate>Mon, 21 Jun 2010 19:41:37 +0000</pubDate>
		<dc:creator>Eran Galperin</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Web development]]></category>
		<category><![CDATA[Zend Framework]]></category>
		<category><![CDATA[zend_date]]></category>
		<category><![CDATA[zend_locale]]></category>

		<guid isPermaLink="false">http://www.techfounder.net/?p=513</guid>
		<description><![CDATA[I'm writing this post as a warning for people using Zend_Date to validate dates, either as a standalone or as a validator in a chain. I've been using Zend_Validate_Date validator, which uses the Zend_Date::isDate() method internally, for a while now - mainly for form processing. I've recently completed a project where a very bizarre bug [...]]]></description>
			<content:encoded><![CDATA[<p>I'm writing this post as a warning for people using Zend_Date to validate dates, either as a standalone or as a validator in a chain. I've been using Zend_Validate_Date validator, which uses the Zend_Date::isDate() method internally, for a while now - mainly for form processing.</p>
<p>I've recently completed a project where a very bizarre bug would occur on some machines, and I wasn't able to reproduce it myself on any test machine that I tried. The bug manifested in a calendar system we built for the project, and in certain situations form submissions would not pass the date validation through Zend_Validate_Date, despite using the exact same input that was passing on the test machines.</p>
<p>After bringing in one of the "affected" machines for testing, I used the process of elimination to determine the problem originates in the Zend_Date::isDate() method, which has different behavior on different machines.</p>
<p>Zend_Date tries to validate dates according to a given format (with a default fallback). The dangerous behavior is that it tries to convert the given format to a localized format using Zend_Locale. Zend_Locale attempts to detect automatically the locale of the requesting client, and it appears that on the machines that were exhibiting the bug, a different locale was determined than those I was testing it on.</p>
<p>Despite trying to validate a non-localized format (to be exact, the MySQL timestamp format - YYYY-MM-dd HH:mm:sss), it was being converted on the affected machine sdue to the auto-detection by Zend_Locale (on a side note - it was happening on Internet Explorer 8 on Windows 7). To avoid this issue, I set the locale for all requests manually to en_US, since I don't have any other use for Zend_Locale in the application. I put the following lines in the bootstrap -</p>
<pre class="php">$locale = new Zend_Locale('en_US');
Zend_Registry::set('Zend_Locale', $locale);
</pre>
<p>As recommended by the manual to achieve this effect.</p>
<p>In my opinion, the Locale should be not be auto-detected by default - it should be an opt-in feature if it affects other components behind the scenes.</p>
 <img src="http://www.techfounder.net/wp-content/plugins/feed-statistics.php?view=1&post_id=513" width="1" height="1" style="display: none;" />
	<div style="">
		<a href="http://twitter.com/share" class="twitter-share-button" data-count="vertical" data-text="On the pitfalls of date validation with the Zend Framework" data-url="http://www.techfounder.net/2010/06/21/on-the-pitfalls-of-date-validation-with-the-zend-framework/"  data-via="erangalperin">Tweet</a>
	</div>
	<script type="text/javascript" src="http://platform.twitter.com/widgets.js"></script>]]></content:encoded>
			<wfw:commentRss>http://www.techfounder.net/2010/06/21/on-the-pitfalls-of-date-validation-with-the-zend-framework/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Multiple row operations in MySQL / PHP</title>
		<link>http://www.techfounder.net/2009/05/14/multiple-row-operations-in-mysql-php/</link>
		<comments>http://www.techfounder.net/2009/05/14/multiple-row-operations-in-mysql-php/#comments</comments>
		<pubDate>Thu, 14 May 2009 01:43:43 +0000</pubDate>
		<dc:creator>Eran Galperin</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Web development]]></category>
		<category><![CDATA[insert]]></category>
		<category><![CDATA[multiple rows]]></category>
		<category><![CDATA[on duplicate key]]></category>
		<category><![CDATA[update]]></category>

		<guid isPermaLink="false">http://www.techfounder.net/?p=229</guid>
		<description><![CDATA[Multiple row operations are in common use in a normalized application databases as one database entity is often linked to multiple sub-entities (for example a user and his tags). By row operations I'm referring to write queries, namely UPDATE and INSERT queries (DELETE is less interesting so I'll leave it out for now). Too often [...]]]></description>
			<content:encoded><![CDATA[<p>Multiple row operations are in common use in a normalized application databases as one database entity is often linked to multiple sub-entities (for example a user and his tags). By row operations I'm referring to write queries, namely UPDATE and INSERT queries (DELETE is less interesting so I'll leave it out for now). </p>
<p>Too often I've seen such queries ran in long loops one at a time, which is very bad for performance (as I will show here) and sometimes equally bad for integrity (if the process is interrupted). So what are the alternatives?<br />
<span id="more-229"></span></p>
<h2>Inserting multiple rows</h2>
<p>Insertion of multiple rows comes about often in batch jobs, database migrations and handling table relationships. A naive approach, via PHP, would be to loop over the data to be inserted, inserting one row at a time. Suppose the data is already properly filtered and quoted:</p>
<pre class="php"><ol><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #b1b100;">foreach</span><span style="color: #66cc66;">&#40;</span> <span style="color: #0000ff;">$data</span> <span style="color: #b1b100;">as</span> <span style="color: #0000ff;">$row</span> <span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#123;</span></div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">      <span style="color: #0000ff;">$query</span> = <span style="color: #ff0000;">&quot;INSERT INTO `test_table` (user_id,content)&quot;</span></div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">          . <span style="color: #ff0000;">&quot; VALUES (&quot;</span></div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">          . <span style="color: #0000ff;">$row</span><span style="color: #66cc66;">&#91;</span><span style="color: #ff0000;">'user_id'</span><span style="color: #66cc66;">&#93;</span> . <span style="color: #ff0000;">&quot;,&quot;</span></div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">          . <span style="color: #0000ff;">$row</span><span style="color: #66cc66;">&#91;</span><span style="color: #ff0000;">'content'</span><span style="color: #66cc66;">&#93;</span> . <span style="color: #ff0000;">&quot;)&quot;</span>;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">      <a href="http://www.php.net/mysql_query"><span style="color: #000066;">mysql_query</span></a><span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">$query</span><span style="color: #66cc66;">&#41;</span>;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #66cc66;">&#125;</span></div></li></ol></pre>
<p>Depending on the amount of rows to be inserted, this can be a costly process. A better approach would be to concatenate the values into one insert query and then execute it:</p>
<pre class="php"><ol><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #0000ff;">$values</span> = <a href="http://www.php.net/array"><span style="color: #000066;">array</span></a><span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span>;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #b1b100;">foreach</span><span style="color: #66cc66;">&#40;</span> <span style="color: #0000ff;">$data</span> <span style="color: #b1b100;">as</span> <span style="color: #0000ff;">$row</span> <span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#123;</span></div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">    <span style="color: #0000ff;">$values</span><span style="color: #66cc66;">&#91;</span><span style="color: #66cc66;">&#93;</span> =  <span style="color: #ff0000;">&quot;(&quot;</span> . <span style="color: #0000ff;">$row</span><span style="color: #66cc66;">&#91;</span><span style="color: #ff0000;">'user_id'</span><span style="color: #66cc66;">&#93;</span> . <span style="color: #ff0000;">&quot;,&quot;</span> . <span style="color: #0000ff;">$row</span><span style="color: #66cc66;">&#91;</span><span style="color: #ff0000;">'content'</span><span style="color: #66cc66;">&#93;</span> . <span style="color: #ff0000;">&quot;)&quot;</span>;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #66cc66;">&#125;</span></div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #b1b100;">if</span><span style="color: #66cc66;">&#40;</span> !<a href="http://www.php.net/empty"><span style="color: #000066;">empty</span></a><span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">$values</span><span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#123;</span></div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">    <span style="color: #0000ff;">$query</span> = <span style="color: #ff0000;">&quot;INSERT INTO `test_table` (user_id,content) VALUES &quot;</span></div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">             . <a href="http://www.php.net/implode"><span style="color: #000066;">implode</span></a><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">','</span>,<span style="color: #0000ff;">$values</span><span style="color: #66cc66;">&#41;</span>;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;">    <a href="http://www.php.net/mysql_query"><span style="color: #000066;">mysql_query</span></a><span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">$query</span><span style="color: #66cc66;">&#41;</span>;</div></li><li style="font-family: 'Courier New', Courier, monospace; color: black; font-weight: normal; font-style: normal;"><div style="font-family: 'Courier New', Courier, monospace; font-weight: normal;"><span style="color: #66cc66;">&#125;</span></div></li></ol></pre>
<p>What's the difference? lets see some benchmarks:<br />
<img src="http://www.techfounder.net/wp-content/uploads/2009/05/time.png" alt="time" title="time" width="485" height="205"  /><br />
A single query completes <b>much</b> faster than looping through multiple queries. At 2560 rows inserted, it took the loop ~36 seconds to complete, yet the single query took just 0.14 seconds.</p>
<p>Memory consumption shows a reverse trend however:<br />
<img src="http://www.techfounder.net/wp-content/uploads/2009/05/memory.png" alt="memory" title="memory" width="481" /><br />
Since the values are concatenated to create the single query, it consumes more and more memory with more rows as it needs to hold a larger query string. At 2560 rows inserted, the single query approach consumed ~800kb in memory, while the loop consumed just ~60kb.</p>
<p>Since memory is much cheaper than CPU cycles and database connections, I'd usually opt for the single query approach. It's important though to be aware of the implications.</p>
<h2>Inserting / Updating (multiple) values</h2>
<p>Another use-case of multiple row operations is when we want to insert several rows that might exist already, and in case they exist we want to update existing values instead. Of course, we could check first which rows exist, update the ones that do and insert the ones that don't - for a total of at least three separate queries. </p>
<p>Inserting and updating in the same operation is done using <a href="http://dev.mysql.com/doc/refman/5.1/en/insert-on-duplicate.html" target="_blank">INSERT ... ON DUPLICATE KEY UPDATE</a>.</p>
<p>The KEY in this statement should be a unique key in the table you are inserting to (otherwise duplicates would be allowed and this operation would be moot). The syntax of this statement allows to pick which columns are updated in the case of matching rows. For example, suppose I'm adding translations for content pages. I have the following table schema:</p>
<pre class="sql">&nbsp;
pages
 - id
 - content
 - ...
&nbsp;
pages_translations
 - page_id
 - lang_id
 - content
&nbsp;</pre>
<p>The pages_translations table has a primary (unique) key on ( page_id , lang_id ). When adding / updating a translation, the query would look something like:</p>
<pre class="sql"><span style="color: #993333; font-weight: bold;">INSERT</span> <span style="color: #993333; font-weight: bold;">INTO</span> <span style="color: #ff0000;">`pages_translations`</span> <span style="color: #66cc66;">&#40;</span>page_id,lang_id,content<span style="color: #66cc66;">&#41;</span>
<span style="color: #993333; font-weight: bold;">VALUES</span> <span style="color: #66cc66;">&#40;</span><span style="color: #cc66cc;">5</span>,<span style="color: #cc66cc;">2</span>,<span style="color: #ff0000;">'the brown fox ...'</span><span style="color: #66cc66;">&#41;</span>
<span style="color: #993333; font-weight: bold;">ON</span> DUPLICATE <span style="color: #993333; font-weight: bold;">KEY</span> <span style="color: #993333; font-weight: bold;">UPDATE</span> content=<span style="color: #993333; font-weight: bold;">VALUES</span><span style="color: #66cc66;">&#40;</span>content<span style="color: #66cc66;">&#41;</span>
&nbsp;</pre>
<p>This will create a translation if one does not exist and update the content field if it does exist. We can combine this statement with the previous approach for multiple insertion to insert / update multiple rows with one statement.</p>
<p>Updating multiple rows can be done using this technique, or through a CASE .. WHEN condition for each row. This method is a bit more involved and usually results in bigger queries but it's worth noting. Example:</p>
<pre class="sql"><span style="color: #993333; font-weight: bold;">UPDATE</span> <span style="color: #ff0000;">`pages`</span> <span style="color: #993333; font-weight: bold;">SET</span> content=<span style="color: #66cc66;">&#40;</span>CASE
    WHEN id=<span style="color: #cc66cc;">1</span> THEN <span style="color: #ff0000;">'first page content ...'</span>
    WHEN id=<span style="color: #cc66cc;">2</span> THEN <span style="color: #ff0000;">'second page content ...'</span>
END<span style="color: #66cc66;">&#41;</span></pre>
<p>We need to repeat this format for each row which results is a somewhat verbose query.</p>
<h2>Inserting / Updating with multiple tables</h2>
<p>There are some cases where we would like to use data in one or several tables to update / insert into another table. </p>
<p>With insertion the <a href="http://dev.mysql.com/doc/refman/5.1/en/insert-select.html" target="_blank">syntax is relatively straightforward</a> - replace the VALUES part of the statement with a SELECT statement. </p>
<pre class="sql"><span style="color: #993333; font-weight: bold;">INSERT</span> <span style="color: #993333; font-weight: bold;">INTO</span> <span style="color: #ff0000;">`my_table`</span> <span style="color: #66cc66;">&#40;</span>col1,col2,col3<span style="color: #66cc66;">&#41;</span>
<span style="color: #993333; font-weight: bold;">SELECT</span> col4,col5,col6
<span style="color: #993333; font-weight: bold;">FROM</span> <span style="color: #ff0000;">`another_table`</span></pre>
<p>The ON DUPLICATE KEY condition can be used with this statement as well.</p>
<p>Updating rows cross-tables is done with a JOIN statement (of the less declarative type).</p>
<pre class="sql"><span style="color: #993333; font-weight: bold;">UPDATE</span> <span style="color: #ff0000;">`my_table`</span>,<span style="color: #ff0000;">`other_table`</span>
   <span style="color: #993333; font-weight: bold;">SET</span> <span style="color: #ff0000;">`my_table`</span>.col1 = <span style="color: #ff0000;">`other_table`</span>.col2
<span style="color: #993333; font-weight: bold;">WHERE</span> <span style="color: #ff0000;">`my_table`</span>.parent_id = <span style="color: #ff0000;">`other_table`</span>.id</pre>
<p>It's important to try the INSERT / UPDATE select statements separately to determine how many rows they will select and how fast will they complete. INSERT / UPDATE operations are relatively costly and can be a severe bottleneck for the database if not managed correctly.</p>
 <img src="http://www.techfounder.net/wp-content/plugins/feed-statistics.php?view=1&post_id=229" width="1" height="1" style="display: none;" />
	<div style="">
		<a href="http://twitter.com/share" class="twitter-share-button" data-count="vertical" data-text="Multiple row operations in MySQL / PHP" data-url="http://www.techfounder.net/2009/05/14/multiple-row-operations-in-mysql-php/"  data-via="erangalperin">Tweet</a>
	</div>
	<script type="text/javascript" src="http://platform.twitter.com/widgets.js"></script>]]></content:encoded>
			<wfw:commentRss>http://www.techfounder.net/2009/05/14/multiple-row-operations-in-mysql-php/feed/</wfw:commentRss>
		<slash:comments>31</slash:comments>
		</item>
		<item>
		<title>Zend Framework certification</title>
		<link>http://www.techfounder.net/2008/12/30/zend-framework-certification/</link>
		<comments>http://www.techfounder.net/2008/12/30/zend-framework-certification/#comments</comments>
		<pubDate>Tue, 30 Dec 2008 17:59:07 +0000</pubDate>
		<dc:creator>Eran Galperin</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[techfounder]]></category>
		<category><![CDATA[Web development]]></category>
		<category><![CDATA[Zend Framework]]></category>

		<guid isPermaLink="false">http://www.techfounder.net/?p=195</guid>
		<description><![CDATA[Today I took and passed the Zend Framework certification exam. A possible project involving Zend might be materializing in the near future, and this was a requirement from one of my contractors (OpenIT). Being that they offered to sponsor the cost, I had no reason not take it. As I found no concrete online information [...]]]></description>
			<content:encoded><![CDATA[<p>Today I took and passed the <a href="http://www.zend.com/en/services/certification/framework/">Zend Framework certification exam</a>. A possible project involving Zend might be materializing in the near future, and this was a requirement from one of my contractors (<a href="http://www.openit.co.il/">OpenIT</a>). Being that they offered to sponsor the <a href="http://www.zend.com/en/store/php-certification/zend-framework-certification-exam-voucher">cost</a>, I had no reason not take it.</p>
<p>As I found no concrete online information on the test (and the <a href="http://downloads.zend.com/framework/generic/ZFC_Study_Guide_v1.pdf">guide link</a> from Zend doesn't work), I might as well elaborate a little for the benefit of future test takers reading this blog -<br />
<span id="more-195"></span><br />
The test is 1.5 hours long and composed of 75 questions. Most questions are multiple choice with the rest being open-ended (usually requiring to enter what you believe will the be the result input from several manipulations). The scope of the test is pretty encompassing, touching some modules that I wouldn't normally use (or even imagine a <a href="http://framework.zend.com/manual/en/zend.memory.html">possible use scenario for</a>), but if you have enough experience of the core features (MVC, Db, Cache, Filter/Validation, Localization / Internationalization and Security) and coding standards - you should do just fine.</p>
<p>The one thing to watch out for is the relatively high percentage of trick questions - which actually made the test somewhat harder than I'd anticipated (after you hit several trick questions in a row, you start being suspicious of every question). Some questions didn't even have an absolutely right answer, but sort of the answer of least incorrectness.</p>
<p>Thankfully, 1.5 hours is plenty long for delibrating some of those more ambigious questions - I had finished my first run in about 35 minutes and rechecked everything in 10 more minutes - leaving me 45 minutes to spare.</p>
<p>I do have some more respect for people holding the certification now (regarding knowledge and experience), though I'm not sure what regard does it hold in the industry (this is the first time I've encountered someone asking for it - and the client is Zend itself, so it's not surprising). If anyone has had previous experience with ZF certification qualification requirements, I would love to hear it.</p>
 <img src="http://www.techfounder.net/wp-content/plugins/feed-statistics.php?view=1&post_id=195" width="1" height="1" style="display: none;" />
	<div style="">
		<a href="http://twitter.com/share" class="twitter-share-button" data-count="vertical" data-text="Zend Framework certification" data-url="http://www.techfounder.net/2008/12/30/zend-framework-certification/"  data-via="erangalperin">Tweet</a>
	</div>
	<script type="text/javascript" src="http://platform.twitter.com/widgets.js"></script>]]></content:encoded>
			<wfw:commentRss>http://www.techfounder.net/2008/12/30/zend-framework-certification/feed/</wfw:commentRss>
		<slash:comments>22</slash:comments>
		</item>
	</channel>
</rss>

