<?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>All Free Tech &#187; PHP</title>
	<atom:link href="http://www.allfreetech.com/category/php/feed" rel="self" type="application/rss+xml" />
	<link>http://www.allfreetech.com</link>
	<description>For developers</description>
	<lastBuildDate>Tue, 27 Jul 2010 13:58:10 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>Web Languages: Php Vs. Asp.net</title>
		<link>http://www.allfreetech.com/php/web-languages-php-vs-asp-net-217.html</link>
		<comments>http://www.allfreetech.com/php/web-languages-php-vs-asp-net-217.html#comments</comments>
		<pubDate>Sat, 22 May 2010 03:35:20 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[ASP.NET]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Languages]]></category>

		<guid isPermaLink="false">http://www.allfreetech.com/?p=217</guid>
		<description><![CDATA[ASP.NET If you program in ASP.NET you&#39;ll typically get too responses from the other side. Either you&#39;re rich (or your company is) or you&#39;re a Microsoft lover. While the name comes from Microsoft&#39;s old ASP technology, they made a huge leap with the .NET Framework, and the CLR allows you to use other languages for [...]]]></description>
			<content:encoded><![CDATA[<p><b>ASP.NET</b><br />
	If you program in ASP.NET you&#39;ll typically get too responses from the other side. Either you&#39;re rich (or your company is) or you&#39;re a Microsoft lover. While the name comes from Microsoft&#39;s old ASP technology, they made a huge leap with the .NET Framework, and the CLR allows you to use other languages for back end processing: typically Visual Basic.NET or C#.</p>
<p>	ASP.NET&#39;s strength lies in object oriented features, and it&#39;s flexibility. Because of the CLR you can have C# programmers and VB.NET programmers working on the same project, or switch languages half way through and not have to rewrite all of your old classes. The .NET class library is organized into inheritable classes based around particular tasks, such as working with XML or image manipulation, so a lot of the more common tasks have been already handled for you.</p>
<p>	Visual Studio .NET is a massive development IDE that (as long as your computer is fast enough) will shave tons of time of your coding. It has built in debugging along with IntelliSense, which allows for auto-completion of methods and variables so you don&#39;t have to memorize everything.</p>
<p>	On the down side, ASP.NET is expensive. One it uses tons more resources on the web server so you&#39;ll require either better server or more servers in the farm. Windows 2003 and Visual Studio .NET are pretty tough on the pocket book as well. It&#39;s extremely rare for an ASP.NET app not to be running on IIS. And if you pay attention to any of the bug reports, you&#39;ll notice that Windows and IIS have had a bit of a history with vulnerabilities being exploited.</p>
<p>	<b>PHP</b><br />
	PHP works in combination of HTML to display dynamic elements on the page. PHP only parses code within its delimiters, such as . Anything outside its delimiters is sent directly to the output and not parsed by PHP. </p>
<p>	PHP strength lies mostly in LAMP. The LAMP architecture has become popular in the Web industry as a way of deploying inexpensive, reliable, scalable, secure web applications. PHP is commonly used as the P in this bundle alongside Linux, Apache and MySQL. PHP can be used with a large number of relational database management systems, runs on all of the most popular web servers and is available for many different operating systems. This flexibility means that PHP has a wide installation base across the Internet; over 18 million Internet domains are currently hosted on servers with PHP installed.</p>
<p>	With PHP 5 finally came exception handling and true OOP, but it still lack namespacing to prevent class naming collisions. PHP&#39;s type checking is very loose, potentially causing problems. Another drawback is that variables in PHP are not really considered to have a type. Finally, for some reason big corporations feel that if they&#39;re not paying for something, then it&#39;s not worth buying. If that&#39;s you&#39;re company&#39;s mentality, they just need to wake up and check out all the awesome free software that&#39;s out there.</p>
<p>	<b>So Which Is Better?</b><br />
	We&#39;ll I have my opinions and you may have yours as well. But in general, PHP is cheap, secure, fast, and reliable, while ASP.NET has quicker development time and is easier due to its class library system can probably be maintained more easily. Both are great languages, and it&#39;s up to you to make the decision.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.allfreetech.com/php/web-languages-php-vs-asp-net-217.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Managing Discounts, Vouchers, and Referrals with PHP 5 Ecommerce</title>
		<link>http://www.allfreetech.com/php/managing-discounts-vouchers-and-referrals-with-php-5-ecommerce-126.html</link>
		<comments>http://www.allfreetech.com/php/managing-discounts-vouchers-and-referrals-with-php-5-ecommerce-126.html#comments</comments>
		<pubDate>Sun, 24 Jan 2010 07:58:15 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[ecommerce]]></category>

		<guid isPermaLink="false">http://www.allfreetech.com/?p=126</guid>
		<description><![CDATA[Once your shipping and tax issues taken into account, the next logical step for your e-commerce store is discount codes, as these need to be entered at the shopping basket stage. Going hand in hand with discount codes are voucher codes and referral discounts. In this article by Michael Peacock, you will learn: How to [...]]]></description>
			<content:encoded><![CDATA[<p>Once your shipping and tax issues taken into account, the next logical step for your e-commerce store is discount codes, as these need to be entered at the shopping basket stage. Going hand in hand with discount codes are voucher codes and referral discounts. In this article by <b>Michael Peacock</b>, you will learn:</p>
<ul>
<li>How to create a discount code system
<ol>
<li>How to offer different types of discounts</li>
<li>How to take the discount into account at the shopping basket stage</li>
</ol>
</li>
<li>How to sell voucher codes on your store</li>
<li>How to offer discounts to customers who bring us referral business</li>
</ul>
<h1>Discount codes</h1>
<p>Discount codes are a great way to both entice new customers into a store, and also to help retain customers with special discounts. The discount code should work by allowing the customer to enter a code, which will then be verified by the store, and then a discount will be applied to the order.</p>
<p>The following are discount options we may wish to have available in our store:</p>
<ul>
<li>A fixed amount deducted from the cost of the order</li>
<li>A fixed percentage deducted from the cost of the order</li>
<li>The shipping cost altered, either to free or to a lower amount</li>
<li>Product-based discounts (although we won&#39;t cover this one in the article)</li>
</ul>
<p>It may also be useful to take into account the cost of the customer&#39;s basket; after all if we have a $5 discount code, we probably wouldn&#39;t want that to apply for orders of $5 or lower, and may wish to apply a minimum order amount.</p>
<h2>Discount codes data</h2>
<p>When storing discount codes in the framework, we need to store and account for:</p>
<ul>
<li>The voucher code itself, so that we can check that the customer is entering a valid code</li>
<li>Whether the voucher code is active, as we may wish to prepare some voucher codes, but not have them usable until a certain time, or we may wish to discontinue a code</li>
<li>A minimum value for the customer&#39;s basket, either as an incentive for the customer to purchase more or to prevent loss-making situations (for example a $10 discount on a $5 purchase!)</li>
<li>The type of discount:
<ol>
<li><b>Percentage</b>: To indicate that the discount amount is a percentage to be removed from the cost</li>
<li><b>Fixed amount deducted</b>: To indicate that the discount amount is a fixed amount to be removed from the order total</li>
<li><b>Fixed amount set to shipping</b>: To indicate that the discount amount is to be the new value for the shipping cost</li>
</ol>
</li>
<li>Discount amount; that is, the amount of discount to be applied</li>
<li>The number of vouchers issued, if we wish to limit the number of uses of a particular voucher code</li>
<li>An expiry date, so that if we wish to have the voucher code expire, codes with a date after the stored expiry date would no longer work</li>
</ul>
<h3>Discount codes database</h3>
<p>The following table illustrates this information as database fields within a table:</p>
<p>The default value for <i>num_vouchers</i> is <i>-1</i>, which we will use for vouchers that are not limited to a set number of issues.</p>
<p><center></p>
<p>
<style type="text/css">
<!--
@page { margin: 0.79in }
P { margin-bottom: 0.08in }
-->
</style>
</p>
<table border="1" bordercolor="#00000a" cellpadding="7" cellspacing="0" width="542">
<col width="130" />
<col width="122" />
<col width="246" />
<tbody>
<tr valign="TOP">
<td width="130">
<p><font face="Verdana, sans-serif"><font size="2"><b>Field</b></font></font></p>
</td>
<td width="122">
<p><font face="Verdana, sans-serif"><font size="2"><b>Type</b></font></font></p>
</td>
<td width="246">
<p><font face="Verdana, sans-serif"><font size="2"><b>Description</b></font></font></p>
</td>
</tr>
<tr valign="TOP">
<td width="130">
<p><font face="Verdana, sans-serif"><font size="2">ID</font></font></p>
</td>
<td width="122">
<p><font face="Verdana, sans-serif"><font size="2">Integer (Primary Key, Auto increment)</font></font></p>
</td>
<td width="246">
<p><font face="Verdana, sans-serif"><font size="2">For the framework to reference the code</font></font></p>
</td>
</tr>
<tr valign="TOP">
<td width="130">
<p><font face="Verdana, sans-serif"><font size="2">Vouchercode</font></font></p>
</td>
<td width="122">
<p><font face="Verdana, sans-serif"><font size="2">Varchar</font></font></p>
</td>
<td width="246">
<p><font face="Verdana, sans-serif"><font size="2">The code the customer enters into the order</font></font></p>
</td>
</tr>
<tr valign="TOP">
<td width="130">
<p><font face="Verdana, sans-serif"><font size="2">Active</font></font></p>
</td>
<td width="122">
<p><font face="Verdana, sans-serif"><font size="2">Boolean</font></font></p>
</td>
<td width="246">
<p><font face="Verdana, sans-serif"><font size="2">If the code can be used</font></font></p>
</td>
</tr>
<tr valign="TOP">
<td width="130">
<p><font face="Verdana, sans-serif"><font size="2">Min_basket_cost</font></font></p>
</td>
<td width="122">
<p><font face="Verdana, sans-serif"><font size="2">Float</font></font></p>
</td>
<td width="246">
<p><font face="Verdana, sans-serif"><font size="2">The minimum cost of the customer&#39;s basket for the code to work for them</font></font></p>
</td>
</tr>
<tr valign="TOP">
<td width="130">
<p><font face="Verdana, sans-serif"><font size="2">Discount_operation</font></font></p>
</td>
<td width="122">
<p><font face="Verdana, sans-serif"><font size="2">ENUM(&#39;-&#39;,%&#39;,&#39;s&#39;)</font></font></p>
</td>
<td width="246">
<p><font face="Verdana, sans-serif"><font size="2">The type of discount</font></font></p>
</td>
</tr>
<tr valign="TOP">
<td width="130">
<p><font face="Verdana, sans-serif"><font size="2">Num_vouchers</font></font></p>
</td>
<td width="122">
<p><font face="Verdana, sans-serif"><font size="2">Integer</font></font></p>
</td>
<td width="246">
<p><font face="Verdana, sans-serif"><font size="2">Number of times the voucher can be used</font></font></p>
</td>
</tr>
<tr valign="TOP">
<td width="130">
<p><font face="Verdana, sans-serif"><font size="2">Expiry</font></font></p>
</td>
<td width="122">
<p><font face="Verdana, sans-serif"><font size="2">timestamp</font></font></p>
</td>
<td width="246">
<p><font face="Verdana, sans-serif"><font size="2">The date the voucher code expires, and is no longer usable</font></font></p>
</td>
</tr>
</tbody>
</table>
<p></center></p>
<p>The following code represents this data in our database:</p>
<pre style="margin-left: 40px;">CREATE TABLE `discount_codes` (
`ID` INT( 11 ) NOT NULL AUTO_INCREMENT ,
`vouchercode` VARCHAR( 25 ) NOT NULL ,
`active` TINYINT( 1 ) NOT NULL ,
`min_basket_cost` FLOAT NOT NULL ,
`discount_operation` ENUM( &#39;-&#39;, &#39;%&#39;, &#39;s&#39; ) NOT NULL ,
`discount_amount` FLOAT NOT NULL ,
`num_vouchers` INT( 11 ) NOT NULL DEFAULT &#39;-1&#39;,
`expiry` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ,
PRIMARY KEY ( `ID` )
) ENGINE = INNODB DEFAULT CHARSET = latin1 AUTO_INCREMENT =1;
</pre>
<hr color="#ff9933" noshade="noshade" size="1" />
<h2>Discount codes functionality</h2>
<p>The functionality for discount codes can be encapsulated into a single function; this function should be called when the basket is loaded and updated. (That is, if the customer changes the quantity of products in the basket, we must run this; also, if the customer adds a voucher code to their order, we must obviously call this function.)</p>
<p>&nbsp;</p>
<p>The function needs to:</p>
<ol>
<li>Check to see if the customer has entered a voucher code with their order.</li>
<li>If they have, it must look up the voucher code to see if it exists, or doesn&#39;t exist.</li>
<li>If the voucher code exists, it must check to see if the code has expired and is still able to be used (that is, that <i>num_vouchers</i> is greater than <i>0</i> or equal to <i>-1</i>).</li>
<li>Assuming this is the case, it must then check to see that the customer&#39;s basket cost is at least that of the minimum order amount in the discount codes record in the database.</li>
<li>If the voucher is able to be used, it must then determine the type of voucher, and make the relevant discount to either the basket cost, or the shipping costs.</li>
</ol>
<p>The following code does all of this; some of the relevant sections described are highlighted within the code:</p>
<pre style="margin-left: 40px;">/**
  * Consider and apply voucher codes
  * Types of voucher code
  * - = FIXED AMOUNT OFF
  * % = PERCENTAGE OFF
  * s = SET NEW SHIPPING COST
  * @param voucherCode String
  * @return void
  */
  private function considerVouchers( $voucherCode )
  {
    <span style="font-weight: bold;">//The voucher code value is checked to ensure it is not empty </span>
<span style="font-weight: bold;">    // (as per point 1)</span>
<span style="font-weight: bold;">    if( $voucherCode != &#39;&#39; )</span>
<span style="font-weight: bold;">    {</span>
<span style="font-weight: bold;">      // we need the date, to see if the voucher has expired</span>
<span style="font-weight: bold;">      $cts = date(&#39;Y-m-d H:i:s&#39;);</span>
<span style="font-weight: bold;">      $voucher_sql = &quot;SELECT *, if(&#39;{$cts}&#39; &gt; expiry, 1, 0)</span>
<span style="font-weight: bold;">                        AS expired FROM discount_codes</span>
<span style="font-weight: bold;">                      WHERE vouchercode=&#39;{$voucherCode}&#39; LIMIT 1&quot;;</span>
      $this-&gt;registry-&gt;getObject(&#39;db&#39;)-&gt;executeQuery( $voucher_sql );
      if( $this-&gt;registry-&gt;getObject(&#39;db&#39;)-&gt;numRows() == 0 )
      {
        $this-&gt;voucher_notice = &#39;Sorry, the voucher code you entered
            is invalid&#39;;
      }
      else
      {
        $voucher = $this-&gt;registry-&gt;getObject(&#39;db&#39;)-&gt;getRows();
        if( $voucher[&#39;active&#39;] == 1 )
        {
          if( $voucher[&#39;expired&#39;] == 1 )
          {
            $this-&gt;voucher_notice = &#39;Sorry, this voucher has
                expired&#39;;
            return false;
          }
          else
          {
            <span style="font-weight: bold;">// check to see there are some vouchers, and customer</span>
<span style="font-weight: bold;">            // has enough in their basket (points 3 and 4)</span>
            if( $voucher[&#39;num_vouchers&#39;] != 0 )
            {
              <span style="font-weight: bold;">if( $this-&gt;cost &gt;= $voucher[&#39;min_basket_cost&#39;] )</span>
              {
                $this-&gt;discountCode = $voucherCode;
                $this-&gt;discountCodeId = $voucher[&#39;ID&#39;];
                // If the discount operation is a percentage, then
                // the discount value is applied to the basket cost
                // to calculate that percentage. This amount is then
                // deducted from the order cost, giving a &quot;discount
                // value&quot;% discount from the order.
                if( $voucher[&#39;discount_operation&#39;] == &#39;%&#39; )
                {
                  $this-&gt;cost = $this-&gt;
              cost - (($this-&gt;cost)/100)*$voucher[&#39;discount_amount&#39;];
                  $this-&gt;voucher_notice = &#39;A &#39;
                      . $voucher[&#39;discount_amount&#39;]
                      . &#39;% discount has been applied to your order&#39;;
                  return true;
                  // If the discount operation is a subtraction, then
                  // the discount amount from the discount code is
                  // deducted from the order cost, and the order cost
                  // is updated to reflect this.
                }
                elseif( $voucher[&#39;discount_operation&#39;] == &#39;-&#39; )
                {
                  $this-&gt;cost =
                      $this-&gt;cost - $voucher[&#39;discount_amount&#39;];
                  $this-&gt;voucher_notice = &#39;A discount of &amp;pound;&#39;
                      . $voucher[&#39;discount_amount&#39;]
                      . &#39; has been applied to your order&#39;;
                  return true;
                  // Finally, if the discount operation is set to s
                  // then, we set the shipping cost to the discount
                  // value. This could allow us to set free shipping,
                  // or just reduce shipping costs.
                }
                elseif( $voucher[&#39;discount_operation&#39;] == &#39;s&#39; )
                {
                  $this-&gt;shipping_cost = $voucher[&#39;discount_amount&#39;];
                  $this-&gt;voucher_notice = &#39;Your orders shipping cost
                      has been reduced to &amp;pound;&#39;
                      . $voucher[&#39;discount_amount&#39;];
                  return true;
                }
              }
              else
              {
                $this-&gt;voucher_notice = &#39;Sorry, your order total is
                    not enough for your order to qualify for this
                    discount code&#39;;
                return false;
              }
            }
            else
            {
              $this-&gt;voucher_notice = &#39;Sorry, this was a limited
                  edition voucher code, there are no more instances
                  of that code left&#39;;
              return false;
            }
          }
        }
        else
        {
          $this-&gt;voucher_notice = &#39;Sorry, the vocuher code you
              entered is no longer active&#39;;
          return false;
        }
      }
    }
  }
</pre>
<p>Now, we have the basis for our discount code feature. We can issue discount codes as an incentive to get new customers, or to encourage existing customers to make more purchases.</p>
<p>&nbsp;</p>
<h3>Reducing the number of codes available</h3>
<p>One feature we added to the database structure for our discount codes was the ability to limit the number of times a voucher could be used, effectively allowing a code to be &quot;issued&quot; a set number of times. We need to take this into account, and reduce the number of vouchers in circulation, once one has been used.</p>
<p>This won&#39;t be done at this stage, but will need to be implemented at the final checkout stage when an order is updated to &quot;paid&quot;, or the customer pays online. The following function will do this for us; it checks to see if a code is used, or if it&#39;s unlimited or has expired, and if it&#39;s not unlimited and hasn&#39;t already expired, it updates the code to decrement the number of uses remaining:</p>
<pre style="margin-left: 40px;">private function adjustDiscountCodeQuantities( $codeId )
{
$sql = &quot;SELECT num_vouchers FROM discount_codes WHERE ID=&quot; . $codeId;
$this-&gt;registry-&gt;getObject(&#39;db&#39;)-&gt;executeQuery( $sql );
if( $this-&gt;registry-&gt;getObject(&#39;db&#39;)-&gt;numRows() &gt; 0 )
{
$codeData = $this-&gt;registry-&gt;getObject(&#39;db&#39;)-&gt;getRows();
if( $codeData[&#39;num_vouchers&#39;] &gt; 0 )
{
$sql = &quot;UPDATE discount_codes SET num_vouchers=num_vouchers-1
WHERE ID=&quot; . $codeId;
$this-&gt;registry-&gt;getObject(&#39;db&#39;)-&gt;executeQuery( $sql );
}
}
}
</pre>
<h1>Purchasable voucher codes</h1>
<p>Voucher codes work in the same way as discount codes, except that they are purchased for use by a customer, as opposed to given away for promotional reasons.</p>
<h2>Existing functionality</h2>
<p>The discount codes and product variation features already give us most of the functionality required for purchasable voucher codes.</p>
<h3>Discount codes</h3>
<p>As a voucher code has the same functionality as our discount codes, which we built earlier, we have a large portion of the functionality in place. When a voucher is purchased, a new record in the discount codes table will be made, with a <i>num_vouchers</i> value of <i>1</i>.</p>
<h3>Product variations</h3>
<p>Let&#39;s say we have product variations feature built, which allows us to create a purchasable voucher code product, with variations that increase the price in increments, perhaps of $5.</p>
<h2>Required additional functionality</h2>
<p>We only need to add additional functionality to this if we wish to automate the process of generating a voucher code when a customer purchases a voucher code. Without additional functionality, we would simply notice a new order, manually create a new discount code, and then e-mail the customer with the code. However, automating this would save us quite a lot of time, so let&#39;s look at what would be involved in doing this:</p>
<ol>
<li>First, we must wait until an order has its status updated to &quot;paid&quot;. This would either be when a payment is made online by the customer, or when an offl ine payment is received and we, as the administrator, mark the order as &quot;paid&quot;.</li>
<li>The order contents must be searched for anything that is a purchasable voucher code.</li>
<li>For each of these vouchers purchased, a new record must be automatically inserted into the <i>voucher_codes</i> table with the following values:
<ul>
<li><i>vouchercode</i>: A randomly generated string</li>
<li><i>active</i>: <i>1</i></li>
<li><i>min_basket_cost</i>: The amount of the voucher purchased</li>
<li><i>discount_operation</i>: (because we are subtracting an amount from the cost)</li>
<li><i>discount_amount</i>: The amount of the voucher purchased</li>
<li><i>num_vouchers</i>: <i>1</i></li>
<li><i>expiry</i>: A year in the future would be a suitable validity period</li>
</ul>
</li>
<li>These vouchers would then be sent through e-mail to the customer.</li>
</ol>
<h1>Referrals</h1>
<p>To reward loyal customers, we may wish to offer referral discounts to them. This would work by encouraging our customers to introduce new customers, and giving them a credit based off a percentage of orders placed by customers they refer to our store. Customers referring other customers would be given a referral code, which could either be part of a link used to promote the site, or given to customers to enter somewhere on the order process.</p>
<p>The referral process should work like this:</p>
<ol>
<li>Check for a referral code in the URL.</li>
<li>If a code is found in the URL, it should be stored in a cookie, unless a previous referral code is already being stored; in that case, the older code should be used.</li>
<li>At the checkout stage, this code should be looked up, to check if it is valid and to see the commission to be applied to the customer who passed the referral. The order number, commission value, and referring customer should be stored in a database table.</li>
<li>When the order is updated to &quot;paid&quot; or &quot;paid online&quot;, the commission should be applied to the relevant customer&#39;s account.</li>
</ol>
<h2>Database changes</h2>
<p>This requires a new database table, as well as some database alterations to work.</p>
<h3>New table: Referrers</h3>
<p>A referrers table is required to link customers to a unique referral code, and a commission percentage. It would require the following fields:</p>
<p><center></p>
<p>
<style type="text/css">
<!--
@page { margin: 0.79in }
P { margin-bottom: 0.08in }
-->
</style>
</p>
<table border="1" bordercolor="#00000a" cellpadding="7" cellspacing="0" width="542">
<col width="89" />
<col width="99" />
<col width="310" />
<tbody>
<tr valign="TOP">
<td width="89">
<p><font face="Verdana, sans-serif"><font size="2"><b>Field</b></font></font></p>
</td>
<td width="99">
<p><font face="Verdana, sans-serif"><font size="2"><b>Type</b></font></font></p>
</td>
<td width="310">
<p><font face="Verdana, sans-serif"><font size="2"><b>Description</b></font></font></p>
</td>
</tr>
<tr valign="TOP">
<td width="89">
<p><font face="Verdana, sans-serif"><font size="2">Customer ID</font></font></p>
</td>
<td width="99">
<p><font face="Verdana, sans-serif"><font size="2">Integer </font></font></p>
</td>
<td width="310">
<p><font face="Verdana, sans-serif"><font size="2">A reference to the customer&#39;s ID number</font></font></p>
</td>
</tr>
<tr valign="TOP">
<td width="89">
<p><font face="Verdana, sans-serif"><font size="2">Referral Code</font></font></p>
</td>
<td width="99">
<p><font face="Verdana, sans-serif"><font size="2">Varchar (Unique)</font></font></p>
</td>
<td width="310">
<p><font face="Verdana, sans-serif"><font size="2">The customer&#39;s referral code, which he/she would give to friends</font></font></p>
</td>
</tr>
<tr valign="TOP">
<td width="89">
<p><font face="Verdana, sans-serif"><font size="2">Commission percentage</font></font></p>
</td>
<td width="99">
<p><font face="Verdana, sans-serif"><font size="2">Float</font></font></p>
</td>
<td width="310">
<p><font face="Verdana, sans-serif"><font size="2">The percentage of commission that would be credited back to the customer&#39;s account</font></font></p>
</td>
</tr>
<tr valign="TOP">
<td width="89">
<p><font face="Verdana, sans-serif"><font size="2">Active</font></font></p>
</td>
<td width="99">
<p><font face="Verdana, sans-serif"><font size="2">Boolean</font></font></p>
</td>
<td width="310">
<p><font face="Verdana, sans-serif"><font size="2">If the customer is a referrer, or not, as we may wish to disable a customer&#39;s referral code, if he/she is abusing it</font></font></p>
</td>
</tr>
</tbody>
</table>
<p></center></p>
<h3>Changes</h3>
<p>Customers should have a credit field, showing how much credit they have with the store based on referred customers. The orders table should also be altered to store a record of any referral code used.</p>
<h2>Functionality</h2>
<p>The workflow of this feature would work like this:</p>
<ol>
<li>An order is placed, and a referral code is associated with it.</li>
<li>The order is marked as &quot;paid&quot;.</li>
<li>A lookup is performed on the referrers table to find the customer and the percentage.</li>
<li>The commission value is calculated.</li>
<li>The referring customer&#39;s credit value is updated to include new commission earned.</li>
</ol>
<p>We could extend this to record a list of transactions to a customer&#39;s credit, such as dates and amounts their account was credited by, and allow reports to be generated and sent to them. However, that could occupy several chapters itself!</p>
<h3>Checkout process consideration</h3>
<p>This requires an important consideration when we get to the checkout stage: we must take into account any credit stored on a customer&#39;s account. If they have credit, it should be deducted from any of their own orders, to ensure they can spend the commission they earned.</p>
<h1>Summary</h1>
<p>We now have a number of useful features to encourage new orders and customers to our store, as well as encouraging existing customers to promote our store through affiliate codes.</p>
<p>This included creating a voucher code system which supported vouchers that deducted a percentage from the order total, vouchers that deducted a fixed amount from the order total, and vouchers that altered the cost of shipping to the customer. These vouchers were able to take into account expiry dates, limited-use options, and the current cost of the customer&#39;s basket.</p>
<p>Taking this forward, we looked at how to extend our framework to allow customers to purchase vouchers as gifts for other customers, as well as how to extend our store to support referral bonuses and incentives for our customers.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.allfreetech.com/php/managing-discounts-vouchers-and-referrals-with-php-5-ecommerce-126.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Shipping and Tax Calculations with PHP 5 Ecommerce</title>
		<link>http://www.allfreetech.com/php/shipping-and-tax-calculations-with-php-5-ecommerce-124.html</link>
		<comments>http://www.allfreetech.com/php/shipping-and-tax-calculations-with-php-5-ecommerce-124.html#comments</comments>
		<pubDate>Sun, 24 Jan 2010 07:56:01 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[ecommerce]]></category>

		<guid isPermaLink="false">http://www.allfreetech.com/?p=124</guid>
		<description><![CDATA[In this article by Michael Peacock, you will learn about how to process orders for shipping and tax calculations, once your e-commerce store has received the purchase order. The article includes the following topics: How to calculate shipping costs based on Product, Weight, Location, and &#34;Shipping rules&#34; About third-party shipping APIs How to integrate shipping [...]]]></description>
			<content:encoded><![CDATA[<p>In this article by <b>Michael Peacock</b>, you will learn about how to process orders for shipping and tax calculations, once your e-commerce store has received the purchase order. The article includes the following topics:</p>
<ul>
<li>How to calculate shipping costs based on Product, Weight, Location, and &quot;Shipping rules&quot;</li>
<li>About third-party shipping APIs</li>
<li>How to integrate shipping and tracking notifications on orders</li>
<li>How to integrate tax costs into our system</li>
</ul>
<h1>Shipping</h1>
<p>Shipping is a very important aspect of an e-commerce system; without it customers will not accurately know the cost of their order. The only situation where we wouldn&#39;t want to include shipping costs is where we always offer free shipping. However, in that situation, we could either add provisions to ignore shipping costs, or we could set all values to zero, and remove references to shipping costs from the user interface.</p>
<h2>Shipping methods</h2>
<p>The first requirement to calculate shipping costs is a shipping method. We may wish to offer a number of different shipping methods to our customers, such as standard shipping, next-day shipping, International shipping, and so on.</p>
<p>The system will require a default shipping method, so when the customer visits their basket, they see shipping costs calculated based off the default method. There should be a suitable drop-down list on the basket page containing the list of shipping methods; when this is changed, the costs in the basket should be updated to reflect the selected method.</p>
<p>We should store the following details for each shipping method:</p>
<ul>
<li>An ID number</li>
<li>A name for the shipping method</li>
<li>If the shipping method is active or not, indicating if it should be selectable by customers</li>
<li>If the shipping method is the default method for the store</li>
<li>A default shipping cost, this would:
<ul>
<li>Be pre-populated in a suitable field when creating new products; however, when the product is created through the administration interface, we would store the shipping cost for the product with the product.</li>
<li>Automatically be assigned to existing products in a store when a new shipping method is created to a store that already contains products.</li>
</ul>
</li>
</ul>
<p>This could be suitably stored in our database as the following:</p>
<p><center></p>
<p>
<style type="text/css">
<!--
@page { margin: 0.79in }
P { margin-bottom: 0.08in }
-->
</style>
</p>
<table border="1" bordercolor="#00000a" cellpadding="7" cellspacing="0" width="542">
<col width="79" />
<col width="128" />
<col width="291" />
<tbody>
<tr valign="TOP">
<td bgcolor="#ffffff" style="font-weight: bold;" width="79">
<p align="LEFT"><font face="Verdana, sans-serif"><font size="2">Field</font></font></p>
</td>
<td bgcolor="#ffffff" style="font-weight: bold;" width="128">
<p align="LEFT"><font face="Verdana, sans-serif"><font size="2">Type</font></font></p>
</td>
<td bgcolor="#ffffff" style="font-weight: bold;" width="291">
<p align="LEFT"><font face="Verdana, sans-serif"><font size="2">Description</font></font></p>
</td>
</tr>
<tr valign="TOP">
<td bgcolor="#ffffff" width="79">
<p align="LEFT"><font face="Verdana, sans-serif"><font size="2">ID</font></font></p>
</td>
<td bgcolor="#ffffff" width="128">
<p align="LEFT"><font face="Verdana, sans-serif"><font size="2">Integer, Primary Key, Auto Increment</font></font></p>
</td>
<td bgcolor="#ffffff" width="291">
<p align="LEFT"><font face="Verdana, sans-serif"><font size="2">ID number for the shipping method</font></font></p>
</td>
</tr>
<tr valign="TOP">
<td bgcolor="#ffffff" width="79">
<p align="LEFT"><font face="Verdana, sans-serif"><font size="2">Name</font></font></p>
</td>
<td bgcolor="#ffffff" width="128">
<p align="LEFT"><font face="Verdana, sans-serif"><font size="2">Varchar</font></font></p>
</td>
<td bgcolor="#ffffff" width="291">
<p align="LEFT"><font face="Verdana, sans-serif"><font size="2">The name of the shipping method</font></font></p>
</td>
</tr>
<tr valign="TOP">
<td bgcolor="#ffffff" width="79">
<p align="LEFT"><font face="Verdana, sans-serif"><font size="2">Active</font></font></p>
</td>
<td bgcolor="#ffffff" width="128">
<p align="LEFT"><font face="Verdana, sans-serif"><font size="2">Boolean</font></font></p>
</td>
<td bgcolor="#ffffff" width="291">
<p align="LEFT"><font face="Verdana, sans-serif"><font size="2">Indicates if the shipping method is active</font></font></p>
</td>
</tr>
<tr valign="TOP">
<td bgcolor="#ffffff" width="79">
<p align="LEFT"><font face="Verdana, sans-serif"><font size="2">Default_cost</font></font></p>
</td>
<td bgcolor="#ffffff" width="128">
<p align="LEFT"><font face="Verdana, sans-serif"><font size="2">Float</font></font></p>
</td>
<td bgcolor="#ffffff" width="291">
<p align="LEFT"><font face="Verdana, sans-serif"><font size="2">The default cost for products for this shipping method</font></font></p>
</td>
</tr>
</tbody>
</table>
<p></center></p>
<p>This can be represented in the database using the following SQL:</p>
<pre style="margin-left: 40px;">CREATE TABLE `shipping_methods` (
`ID` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`name` VARCHAR( 50 ) NOT NULL ,
`active` BOOL NOT NULL ,
`is_default` BOOL NOT NULL ,
`default_cost` DOUBLE NOT NULL ,
INDEX ( `active` , `is_default` )
) ENGINE = INNODB COMMENT = &#39;Shipping methods&#39;;
</pre>
<h2>Shipping costs</h2>
<p>There are several different ways to calculate the costs of shipping products to customers:</p>
<ul>
<li>We could associate a cost to each product for each shipping method we have in our store</li>
<li>We could associate costs for each shipping method to ranges of weights, and either charge the customer based on the weight-based shipping cost for each product combined, or based on the combined weight of the order</li>
<li>We could base the cost on the customer&#39;s delivery address</li>
</ul>
<p>The exact methods used, and the way they are used, depends on the exact nature of the store, as there are implications to these methods. If we were to use location-based shipping cost calculations, then the customer would not be aware of the total cost of their order until they entered their delivery address. There are a few ways this can be avoided: the system could assume a default delivery location and associated costs, and then update the customer&#39;s delivery cost at a later stage. Alternatively, if we enabled delivery methods for different locations or countries, we could associate the appropriate costs to these methods, although this does of course rely on the customer selecting the correct shipping method for their order to be approved; appropriate notifications to the customer would be required to ensure they do select the correct ones.</p>
<p>For this article we will implement:</p>
<ul>
<li><b>Weight-based shipping costs</b>: Here the cost of shipping is based on the weight of the products.</li>
<li><b>Product-based shipping costs</b>: Here the cost of shipping is set on a per product basis for each product in the customer&#39;s basket.</li>
</ul>
<p>We will also discuss location-based shipping costs, and look at how we may implement it. To account for international or long-distance shipping, we will use varying shipping methods; perhaps we could use:</p>
<ul>
<li>Shipping within state X.</li>
<li>Shipping outside of state X.</li>
<li>International shipping. (This could be broken down per continent if we wanted, without imposing on the customer too much.)</li>
</ul>
<h3>Product-based shipping costs</h3>
<p>Product-based shipping costs would simply require each product to have a shipping cost associated to it for each shipping method in the store. As discussed earlier, when a new method is added to an existing store, a default value will initially be used, so in theory the administrator only needs to alter products whose shipping costs shouldn&#39;t be the default cost, and when creating new products, the relevant text box for the shipping cost for that method will have the default cost pre-populated.</p>
<p>To facilitate these costs, we need a new table in our database storing:</p>
<ul>
<li>Product IDs</li>
<li>Shipping method IDs</li>
<li>Shipping costs</li>
</ul>
<p>The following SQL represents this table in our database:</p>
<pre style="margin-left: 40px;">CREATE TABLE `shipping_costs_product` (
`shipping_id` int(11) NOT NULL, `product_id` int(11) NOT NULL,
`cost` float NOT NULL, PRIMARY KEY (`shipping_id`,`product_id`) )
ENGINE=InnoDB DEFAULT CHARSET=latin1;
</pre>
<h3>Weight-based shipping costs</h3>
<p>Depending on the store being operated from our framework, we may need to base shipping costs on the weights of products. If a particular courier for a particular shipping method charges based on weights, then there isn&#39;t any point in creating costs for each product for that shipping method. Our framework can calculate the shipping costs based on the weight ranges and costs for the method, and the weight of the product.</p>
<p>Within our database we would need to store:</p>
<ul>
<li>The shipping method in question</li>
<li>A lower bound for the product weight, so we know which cost to apply to a product</li>
<li>A cost associated for anything between this and the next weight bound</li>
</ul>
<p>The table below illustrates these fields in our database:</p>
<p><center></p>
<p>
<style type="text/css">
<!--
@page { margin: 0.79in }
P { margin-bottom: 0.08in }
-->
</style>
</p>
<table border="1" bordercolor="#00000a" cellpadding="7" cellspacing="0" width="542">
<col width="89" />
<col width="128" />
<col width="282" />
<tbody>
<tr valign="TOP">
<td bgcolor="#ffffff" style="font-weight: bold;" width="89">
<p><font face="Verdana, sans-serif"><font size="2">Field</font></font></p>
</td>
<td bgcolor="#ffffff" style="font-weight: bold;" width="128">
<p><font face="Verdana, sans-serif"><font size="2">Type</font></font></p>
</td>
<td bgcolor="#ffffff" style="font-weight: bold;" width="282">
<p><font face="Verdana, sans-serif"><font size="2">Description</font></font></p>
</td>
</tr>
<tr valign="TOP">
<td bgcolor="#ffffff" width="89">
<p><font face="Verdana, sans-serif"><font size="2">ID</font></font></p>
</td>
<td bgcolor="#ffffff" width="128">
<p><font face="Verdana, sans-serif"><font size="2">Integer, primary key, Auto Increment</font></font></p>
</td>
<td bgcolor="#ffffff" width="282">
<p><font face="Verdana, sans-serif"><font size="2">A unique reference for the weight range</font></font></p>
</td>
</tr>
<tr valign="TOP">
<td bgcolor="#ffffff" width="89">
<p><font face="Verdana, sans-serif"><font size="2">Shipping_id</font></font></p>
</td>
<td bgcolor="#ffffff" width="128">
<p><font face="Verdana, sans-serif"><font size="2">Integer</font></font></p>
</td>
<td bgcolor="#ffffff" width="282">
<p><font face="Verdana, sans-serif"><font size="2">The shipping method the range applies to</font></font></p>
</td>
</tr>
<tr valign="TOP">
<td bgcolor="#ffffff" width="89">
<p><font face="Verdana, sans-serif"><font size="2">Lower_weight</font></font></p>
</td>
<td bgcolor="#ffffff" width="128">
<p><font face="Verdana, sans-serif"><font size="2">Float</font></font></p>
</td>
<td bgcolor="#ffffff" width="282">
<p><font face="Verdana, sans-serif"><font size="2">For working out which products this weight range cost applies to</font></font></p>
</td>
</tr>
<tr valign="TOP">
<td bgcolor="#ffffff" width="89">
<p><font face="Verdana, sans-serif"><font size="2">Cost</font></font></p>
</td>
<td bgcolor="#ffffff" width="128">
<p><font face="Verdana, sans-serif"><font size="2">Float</font></font></p>
</td>
<td bgcolor="#ffffff" width="282">
<p><font face="Verdana, sans-serif"><font size="2">The shipping cost for a product of this weight</font></font></p>
</td>
</tr>
</tbody>
</table>
<p></center></p>
<p>The following SQL represents this table:</p>
<pre style="margin-left: 40px;">CREATE TABLE `shipping_costs_weight` (
`ID` int(11) NOT NULL auto_increment,
`shipping_id` int(11) NOT NULL,
`lower_weight` float NOT NULL,
`cost` float NOT NULL,
PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
</pre>
<h3>To think about: Location-based shipping costs</h3>
<p>One thing we should still think about is location-based shipping costs, and how we may implement this. There are two primary ways in which we can do this:</p>
<ul>
<li>Assign shipping costs or cost surpluses/reductions to delivery addresses (either countries or states) and shipping methods</li>
<li>Calculate costs using third-party service APIs</li>
</ul>
<p>These two methods have one issue, which is why we are not going to implement them&mdash;that is the costs are calculated later in the checkout process. We want our customers to be well informed and aware of all of their costs as early as possible.</p>
<p>As mentioned earlier, however, we could get round this by assuming a default delivery location and providing customers with a guideline shipping cost, which would be subject to change based on their delivery address. Alternatively, we could allow customers to select their delivery location region from a drop-down list on the main &quot;shopping basket&quot; page. This way they would know the costs right away.</p>
<h4>Regional shipping costs</h4>
<p>We could look at storing:</p>
<ul>
<li>Shipping method IDs</li>
<li>Region types (states or countries)</li>
<li>Region values (an ID corresponding to a list of states or countries)</li>
<li>A priority (in some cases, we may need to only consider the state delivery costs, and not country costs; in others cases, it may be the other way around)</li>
<li>The associated costs changes (this could be a positive or negative value to be added to a product&#39;s delivery cost, as calculated by the other shipping systems already)</li>
</ul>
<p>By doing this, we can then combine the delivery address with the products and lookup a price alteration, which is applied to the product&#39;s delivery cost, which has already been calculated. Ideally, we would use all the shipping cost calculation systems discussed, to make something as flexible as possible, based on the needs of a particular product, particular shipping method or courier, or of a particular store or business.</p>
<h4>Third-party APIs</h4>
<p>The most accurate method of charging delivery costs, encompassing weights and delivery addresses is via APIs provided by couriers themselves, such as UPS. The following web pages may be of reference:</p>
<ul>
<li><a href="http://www.ups.com/onlinetools" target="_blank">http://www.ups.com/onlinetools</a></li>
<li><a href="http://answers.google.com/answers/threadview/id/429083.html" target="_blank">http://answers.google.com/answers/threadview/id/429083.html</a></li>
</ul>
<p>Using such an API, means our shipping cost would be accurate, assuming our weight values were correct for our products, and we would not over or under charge customers for shipping costs. One additional consideration that third-party APIs may require would be dimensions of products, if their costs are also based on product sizes.</p>
<h2>Shipping rules</h2>
<p>Hopefully by using product and/or weight-based shipping methods, we can provide accurate shipping costs; however, some couriers cap their shipping costs for dispatches, or we may wish to offer incentives such as free shipping on certain orders. We may also find that we need to charge more for shipping, depending on the customer&#39;s location.</p>
<p>To store these rules, we need to record:</p>
<ul>
<li>A name for the rule</li>
<li>The shipping method the rule is associated with</li>
<li>The order of the rule, so if more than one rule were applicable, they would be applied in order</li>
<li>The type of match to perform, either against total product cost, or the shipping cost (product cost would allow us to offer free shipping for orders over $X, and against shipping costs allow us to cap the costs at $Y)</li>
<li>The amount to match against</li>
<li>The operator to compare the match amount against the product or basket cost (this would be an operator such as greater than, less than, less than or equal to, greater than or equal to, not equal to, or equal to)</li>
<li>The rule amount; this would be a value that would be applied to the shipping cost by a rule operator</li>
<li>The rule operator, to determine how the rule amount would be applied to the shipping cost (this would be an operator such as plus, minus, divide by, multiply by, or set value to)</li>
</ul>
<p>The following SQL represents this in our database:</p>
<pre style="margin-left: 40px;">CREATE TABLE ` shipping_rules` (
`ID` int(11) NOT NULL auto_increment,
`shipping_id` int(11) NOT NULL,
`match_amount` float NOT NULL,
`match_type` enum(&#39;shipping&#39;,&#39;products&#39;) NOT NULL,
`match_operator` enum(&#39;&lt;&#39;,&#39;&gt;&#39;,&#39;&lt;=&#39;,&#39;&gt;=&#39;,&#39;&lt;&gt;&#39;,&#39;==&#39;) NOT NULL,
`rule` varchar(255) NOT NULL,
`rule_amount` float NOT NULL,
`rule_operator` enum(&#39;+&#39;,&#39;-&#39;,&#39;=&#39;,&#39;*&#39;,&#39;/&#39;) NOT NULL,
`order` int(11) NOT NULL,
PRIMARY KEY (`ID`),
KEY `shipping_id` (`shipping_id`,`order`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
</pre>
<p>Let&#39;s look at some example shipping rules, and potential values for these.</p>
<h3>Free shipping</h3>
<p>If we wished to offer free shipping to all customers whose orders were greater than or equal to $50, we would use the following values:</p>
<ul>
<li><b>Name</b>: Free Shipping</li>
<li><b>Order</b>: 2 (assuming we use both this and the following rule)</li>
<li><b>Type of match</b>: Product</li>
<li><b>Match amount</b>: 50</li>
<li><b>Match comparison operator</b>: Greater than or equal to</li>
<li><b>Rule amount</b>: 0</li>
<li><b>Rule operator</b>: Set equal to</li>
</ul>
<h3>Capped shipping</h3>
<p>If we wished to cap shipping costs to $20, to ensure no customer paid more than that, we would use the following values:</p>
<ul>
<li><b>Name</b>: Max shipping cost</li>
<li><b>Order</b>: 1</li>
<li><b>Type of match</b>: Shipping</li>
<li><b>Match amount</b>: 20</li>
<li><b>Match comparison operator</b>: Greater than</li>
<li><b>Rule amount</b>: 20</li>
<li><b>Rule operator</b>: Set equal to</li>
</ul>
<p>Of course we can also use these rules to do all sorts of calculations, such as discounted shipping for bulk orders, and so on. We could also extend these rules to take into account delivery locations.</p>
<h2>Tracking</h2>
<p>When products are shipped to customers, they may wish to be informed about tracking information. It may be possible for us to integrate with shipping provider APIs to do this. However, the simplest method (which could also eventually be integrated with such an API) is to allow store administrators to supply a message to the customer when they update an order&#39;s status to &quot;dispatched&quot;.</p>
<h2>Integrating shipping costs into the basket</h2>
<p>We should integrate these shipping cost systems into our framework in the following stages:</p>
<ol>
<li>Prepare list of shipping methods and a default method.</li>
<li>Calculate product-based shipping costs.</li>
<li>Calculate weight-based shipping costs.</li>
<li>Consider shipping rules and adjust shipping costs accordingly.</li>
</ol>
<h3>Shipping methods and a default</h3>
<p>We can store a default shipping method in the framework&#39;s settings. When a customer selects an alternative shipping method, we should store that in an appropriate session variable. At this stage, all we need to do is check if the session variable is set. If the session variable is set, then that is the shipping method we must use; if it is not, then we must use the default shipping method.</p>
<pre style="margin-left: 40px;">// get the shipping method
if( isset( $_SESSION[&#39;shipping_method&#39;] ) )
{
// user-selected
$this-&gt;shippingMethodID = intval( $_SESSION[&#39;shipping_method&#39;] );
}
else
{
// system default
$this-&gt;shippingMethodID = $this-&gt;registry-&gt;
getSetting(&#39;default_shipping_method&#39;);
}
</pre>
<h3>Calculating shipping costs based on products</h3>
<p>To calculate shipping costs based on products, we need to lookup the shipping cost for each product in the basket associated with the current shipping method.</p>
<pre style="margin-left: 40px;">// shipping costs: product based
$shippingCosts = $this-&gt;getShippingProductCosts( $this-&gt;productIDs );
</pre>
<p>Once we have these shipping costs, it is a case of looking up the product ID in the <i>$shippingCosts</i> array to get the shipping cost, and multiplying this by the quantity of the product in the basket.</p>
<pre style="margin-left: 40px;">$this-&gt;shippingCost = $this-&gt;shippingCost + ( $shippingCosts[
$contents[&#39;product_id&#39;] ] * $contents[&#39;product_quantity&#39;] );
</pre>
<h3>Calculating shipping costs based on product weights</h3>
<p>To calculate shipping costs based on product weight, we must build an array of shipping costs based on weight ranges.</p>
<pre style="margin-left: 40px;">// shipping costs: weight based
$weightCosts = $this-&gt;getShippingWeightCosts();
</pre>
<p>Once we have our array of shipping weight costs, while iterating through products in the basket, we then iterate through the ordered weights until we find an upper limit to the product in question. Once found, we get our shipping cost. This cost is then multiplied by the quantity of the product in the basket, and added to the rolling shipping cost.</p>
<pre style="margin-left: 40px;">// shipping costs: weight based
$currentWeight = 0;
while( $weightFound == false )
{
if( $contents[&#39;product_weight&#39;] &gt;=
$weightCosts[$currentWeight][&#39;weight&#39;] )
{
$weightFound = true;
$this-&gt;shippingCost = $this-&gt;shippingCost +
( $weightCosts[$currentWeight][&#39;cost&#39;] *
$contents[&#39;product_quantity&#39;] );
}
else
{
if( count( $weightCosts ) == $currentWeight )
{
// we don&#39;t want to do this forever!
$weightFound = true;
}
else
{
$currentWeight++;
}
}
}
</pre>
<h3>Considering shipping rules, and adjusting prices accordingly</h3>
<p>The final shipping feature is shipping rules; this requires us looking up the shipping rules from the database, and iterating through them. For each rule, we need to check the type of rule, then check if the shipping cost or the basket cost is at least that of the rule amount; if it is, then we perform our rule calculation.</p>
<pre style="margin-left: 40px;">/**
* Takes any shipping rules into account with regards to the shipping
costs
* @return void
*/
private function considerShippingRules()
{
// get the rules
$rules_sql = &quot;SELECT * FROM shipping_rules
WHERE shipping_id={$this-&gt;shippingMethodUD}
ORDER BY `order`&quot;;
$this-&gt;registry-&gt;getObject(&#39;db&#39;)-&gt;executeQuery( $rules_sql );
// go through them
while( $rule = $this-&gt;registry-&gt;getObject(&#39;db&#39;)-&gt;getRows() )
{
// rule depends on the shipping cost
</pre>
<p>Here we have established that the current rule is based on shipping cost, which means we then check to see if the shipping cost meets the rule.</p>
<pre style="margin-left: 40px;">if( $rule[&#39;match_type&#39;] == &#39;shipping&#39; )
{
$match = false;
$match_operator = $rule[&#39;match_operator&#39;];
// check to see our shipping cost meets the rule
if( $match_operator == &#39;==&#39; )
{ if( $this-&gt;shippingCost == $rule[&#39;match_amount&#39;] )
{ $match = true; } }
elseif( $match_operator == &#39;&lt;&gt;&#39; )
{ if( $this-&gt;shippingCost &lt;&gt; $rule[&#39;match_amount&#39;] )
{ $match = true; } }
elseif( $match_operator == &#39;&gt;=&#39; )
{ if( $this-&gt;shippingCost &gt;= $rule[&#39;match_amount&#39;] )
{ $match = true; } }
elseif( $match_operator == &#39;&lt;=&#39; )
{ if( $this-&gt;shippingCost &lt;= $rule[&#39;match_amount&#39;] )
{ $match = true; } }
elseif( $match_operator == &#39;&gt;&#39; )
{ if( $this-&gt;shippingCost &gt; $rule[&#39;match_amount&#39;] )
{ $match = true; } }
elseif( $match_operator == &#39;&lt;&#39; )
{ if( $this-&gt;shippingCost &lt; $rule[&#39;match_amount&#39;] )
{ $match = true; } }
</pre>
<p>If a rule match was found, we then take the rule into account.</p>
<pre style="margin-left: 40px;">if( $match == true )
{
// set the shipping cost based on the rule operator and
// the rule amount
$rule_operator = $rule[&#39;rule_operator&#39;];
if( $rule_operator == &#39;=&#39; )
{ $this-&gt;shippingCost = $rule[&#39;rule_amount&#39;]; }
elseif( $rule_operator == &#39;+&#39; )
{ $this-&gt;shippingCost = $this-&gt;shippingCost
+ $rule[&#39;rule_amount&#39;]; }
elseif( $rule_operator == &#39;-&#39; )
{ $this-&gt;shippingCost = $this-&gt;shippingCost
- $rule[&#39;rule_amount&#39;]; }
elseif( $rule_operator == &#39;*&#39; )
{ $this-&gt;shippingCost = $this-&gt;shippingCost
* $rule[&#39;rule_amount&#39;]; }
elseif( $rule_operator == &#39;/&#39; )
{ $this-&gt;shippingCost = $this-&gt;shippingCost
/ $rule[&#39;rule_amount&#39;]; }
}
}
</pre>
<p>If the product is based on the basket cost, we then do the same as before, except that the rule matching depends on the cost of the shopping basket.</p>
<pre style="margin-left: 40px;">elseif( $rule[&#39;match_type&#39;] == &#39;products&#39; )
{
// rule depends on the basket cost
$match = false;
$match_operator = $rule[&#39;match_operator&#39;];
// check to see our basket cost meets the rule
if( $match_operator == &#39;==&#39; )
{ if( $this-&gt;shippingCost == $rule[&#39;match_amount&#39;] )
{ $match = true; } }
elseif( $match_operator == &#39;&lt;&gt;&#39; )
{ if( $this-&gt;cost &lt;&gt; $rule[&#39;match_amount&#39;] )
{ $match = true; } }
elseif( $match_operator == &#39;&gt;=&#39; )
{ if( $this-&gt;cost &gt;= $rule[&#39;match_amount&#39;] )
{ $match = true; } }
elseif( $match_operator == &#39;&lt;=&#39; )
{ if( $this-&gt;cost &lt;= $rule[&#39;match_amount&#39;] )
{ $match = true; } }
elseif( $match_operator == &#39;&gt;&#39; )
{ if( $this-&gt;cost &gt; $rule[&#39;match_amount&#39;] )
{ $match = true; } }
elseif( $match_operator == &#39;&lt;&#39; )
{ if( $this-&gt;cost &lt; $rule[&#39;match_amount&#39;] )
{ $match = true; } }
if( $match == true )
{
// set the shipping cost based on the rule operator
// and the rule amount
$rule_operator = $rule[&#39;rule_operator&#39;];
if( $rule_operator == &#39;=&#39; )
{ $this-&gt;shippingCost = $rule[&#39;rule_amount&#39;]; }
elseif( $rule_operator == &#39;+&#39; )
{ $this-&gt;shippingCost = $this-&gt;shippingCost
+ $rule[&#39;rule_amount&#39;]; }
elseif( $rule_operator == &#39;-&#39; )
{ $this-&gt;shippingCost = $this-&gt;shippingCost
- $rule[&#39;rule_amount&#39;]; }
elseif( $rule_operator == &#39;*&#39; )
{ $this-&gt;shippingCost = $this-&gt;shippingCost
* $rule[&#39;rule_amount&#39;]; }
elseif( $rule_operator == &#39;/&#39; )
{ $this-&gt;shippingCost = $this-&gt;shippingCost
/ $rule[&#39;rule_amount&#39;]; }
}
}
}
}
</pre>
<hr color="#ff9933" noshade="noshade" size="1" />
<p>&nbsp;</p>
<div class="header">
<h1>Tax</h1>
<p>There are three main ways to tackle tax costs in an e-commerce environment:</p>
<ul>
<li>We include tax in our product prices</li>
<li>We assign tax codes to products to separately calculate and display tax costs</li>
<li>We calculate tax based on the location of the buyer</li>
</ul>
<p>The exact requirement for a particular store depends on the store itself and the laws applicable in that country or state. In some situations we can include the tax for a product in its price; it doesn&#39;t need to be displayed to the customer. In others, we may wish for tax to be shown and calculated for the customer, if they are able to reclaim this tax (for example UK/EU VAT), and in some states in the US different states have different taxes depending on the buyer or seller, where some customers may be taxed, others not, or the tax may be based on the state the seller resides in themselves.</p>
<p>Most situations can be handled by associating products with tax calculations, so let&#39;s focus on that. However, we will also discuss how we may implement a location-based tax system, to charge tax depending on the customer&#39;s delivery or billing address.</p>
<h2>Separately calculating tax values</h2>
<p>We could either have:</p>
<ul>
<li>Tax included in a product price, and a tax rule calculating how much of the product&#39;s price should be tax</li>
<li>Product costs stored without tax, and associated with their relevant tax calculations</li>
</ul>
<p>The main difference to the way tax calculations would need to work and the shipping costs is that tax costs actually need to be integrated before the basket; that is, the products themselves should incorporate tax costs.</p>
<p>We will look at the second of these two options.</p>
<p>This would require:</p>
<ul>
<li>Products to have a tax code associated with them</li>
<li>A table of tax codes to be stored in our database, along with calculation details</li>
</ul>
<p>The tax codes (just a reference for the type of tax; for instance, at the time of writing in the UK we would have: zero-rated VAT&mdash;0%, standard rate VAT&mdash;15%, reduced rate VAT&mdash;5%, and different products may have different tax codes associated with them) would have a calculation value and operation associated with them, similar to our shipping rules, this allows the framework to easily add/subtract/divide/multiply the product cost with the calculation value.</p>
<p>	<center></p>
<p>
<style type="text/css">
<!--
@page { margin: 0.79in }
P { margin-bottom: 0.08in }
-->
</style>
</p>
<table border="1" bordercolor="#00000a" cellpadding="7" cellspacing="0" width="542">
<col width="131" />
<col width="131" />
<col width="237" />
<tbody>
<tr valign="TOP">
<td bgcolor="#ffffff" style="font-weight: bold;" width="131">
<p><font face="Verdana, sans-serif"><font size="2">Field</font></font></p>
</td>
<td bgcolor="#ffffff" style="font-weight: bold;" width="131">
<p><font face="Verdana, sans-serif"><font size="2">Type</font></font></p>
</td>
<td bgcolor="#ffffff" style="font-weight: bold;" width="237">
<p><font face="Verdana, sans-serif"><font size="2">Description</font></font></p>
</td>
</tr>
<tr valign="TOP">
<td bgcolor="#ffffff" width="131">
<p><font face="Verdana, sans-serif"><font size="2">ID</font></font></p>
</td>
<td bgcolor="#ffffff" width="131">
<p><font face="Verdana, sans-serif"><font size="2">Integer, Primary Key, Auto Increment</font></font></p>
</td>
<td bgcolor="#ffffff" width="237">
<p><font face="Verdana, sans-serif"><font size="2">The ID for the tax code</font></font></p>
</td>
</tr>
<tr valign="TOP">
<td bgcolor="#ffffff" width="131">
<p><font face="Verdana, sans-serif"><font size="2">Tax_code</font></font></p>
</td>
<td bgcolor="#ffffff" width="131">
<p><font face="Verdana, sans-serif"><font size="2">Varchar</font></font></p>
</td>
<td bgcolor="#ffffff" width="237">
<p><font face="Verdana, sans-serif"><font size="2">Name of the tax code</font></font></p>
</td>
</tr>
<tr valign="TOP">
<td bgcolor="#ffffff" width="131">
<p><font face="Verdana, sans-serif"><font size="2">Calculation_value</font></font></p>
</td>
<td bgcolor="#ffffff" width="131">
<p><font face="Verdana, sans-serif"><font size="2">Double</font></font></p>
</td>
<td bgcolor="#ffffff" width="237">
<p><font face="Verdana, sans-serif"><font size="2">The value applied to the order cost</font></font></p>
</td>
</tr>
<tr valign="TOP">
<td bgcolor="#ffffff" width="131">
<p><font face="Verdana, sans-serif"><font size="2">Calculation_operation</font></font></p>
</td>
<td bgcolor="#ffffff" width="131">
<p><font face="Verdana, sans-serif"><font size="2">Enum </font></font></p>
</td>
<td bgcolor="#ffffff" width="237">
<p><font face="Verdana, sans-serif"><font size="2">The arithmetic operation applied to the order cost and the calculation value, to compute the tax</font></font></p>
</td>
</tr>
</tbody>
</table>
<p>	</center></p>
<p>The following SQL represents this table:</p>
<pre style="margin-left: 40px;">CREATE TABLE `tax_codes` (
`ID` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
`tax_code` varchar(255) NOT NULL,
`calculation_value` DOUBLE NOT NULL,
`calculation_operation` ENUM( &#39;+&#39;, &#39;-&#39;, &#39;*&#39;, &#39;/&#39;, &#39;=&#39; ) NOT NULL,
INDEX ( `tax_code` )
) ENGINE = INNODB;
</pre>
<p>Again, for example, UK standard rate VAT would have a value of 1.15, and an operation of multiply by.</p>
<p>To create a truly flexible tax system for an e-commerce system would involve a book of its own. The simplest methods that we have discussed are relatively straightforward to implement, especially because we have done some very similar work with our shipping methods.</p>
<h2>To think about: Location-based tax costs</h2>
<p>In some situations, we may have different taxes applicable depending on the locations of the buyers and sellers respective to one another. This may be something we would wish to implement. Advice from a tax professional is recommended to determine if this is required for a particular use or implementation of your framework in a particular store.</p>
<h1>A look at our basket now</h1>
<p>Here&#39;s a view of our final basket:</p>
<p style="text-align: center;"><img src="http://www.packtpub.com/files/images/phpecommerce-article1-image01.png" /></p>
<h1>Summary</h1>
<p>In this article, we discussed different ways to approach shipping costs and tax values for products within our e-commerce store. This included:</p>
<ul>
<li>Creating shipping methods</li>
<li>Creating shipping rules to cap, reduce, wipe, or alter shipping costs based on the cost of a basket, or have the shipping cost otherwise calculated</li>
<li>Setting shipping costs for each product based on the product and the shipping method</li>
<li>Setting shipping costs for products based on weights and the shipping method</li>
<li>How we would introduce tax costs to products</li>
</ul>
<p>&nbsp;</p>
</div>
]]></content:encoded>
			<wfw:commentRss>http://www.allfreetech.com/php/shipping-and-tax-calculations-with-php-5-ecommerce-124.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Creating an Ajax Process Using PHP and Oracle</title>
		<link>http://www.allfreetech.com/php/creating-an-ajax-process-using-php-and-oracle-113.html</link>
		<comments>http://www.allfreetech.com/php/creating-an-ajax-process-using-php-and-oracle-113.html#comments</comments>
		<pubDate>Sat, 23 Jan 2010 07:30:42 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Ajax]]></category>
		<category><![CDATA[Oracle]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[oracle]]></category>

		<guid isPermaLink="false">http://www.allfreetech.com/?p=113</guid>
		<description><![CDATA[For some time now, Ajax (Asynchronous JavaScript and XML), has been all the rage in the Web development world. Coming to the forefront with some of Google&#39;s features (Suggest, Maps, Gmail, and so on), Ajax performs server requests without the user having to submit a form or click on a link. In other words, the [...]]]></description>
			<content:encoded><![CDATA[<p>For some time now, Ajax (Asynchronous JavaScript and XML), has been all the rage in the Web development world. Coming to the forefront with some of Google&#39;s features (Suggest, Maps, Gmail, and so on), Ajax performs server requests without the user having to submit a form or click on a link. In other words, the Web browser can make the server request, and handle the response, without the user doing anything or even knowing that this is happening. The immediacy that Ajax brings is not only a great convenience, but it can also be downright cool.</p>
<p>In this recipe, I discuss all the code necessary to use Ajax to go from a simple Web page to a JavaScript function to an XMLHttpRequest to a PHP script and, finally, to an Oracle database. Along with the code, I do talk about the individual pieces with respect to the whole picture: what each chunk does and why it&#39;s important. By reading this HowTo, you will acquire not only some sample code but also, hopefully, a broader understanding of the whole Ajax concept.</p>
<p><span class="parahead1">Background</span></p>
<p>The example I&#39;ll be working with will be a registration form (or a minimal part of it, anyway). The theory is that I must confirm unique email addresses in the users table so that people cannot register multiple times using the same address. This is normally part of the validation process that comes after the user submits the form to the handling PHP script. But why should the user have to wait until then for this piece of validation? Instead, as soon as the user enters their email address and goes to the next form field, the email address will be immediately validated. But to do so, I still need to query the database, which is where Ajax comes in.</p>
<p>The simplified table structure for this example could be created with the following SQL statement (without the presence of the other tables, I&#39;ve done away with identifying keys and such).</p>
<pre>CREATE TABLE users (
	email VARCHAR2(60)
)
</pre>
<p>Obviously you could expand on this in many ways. For now, though, the important thing is that the sample code assumes you have established such a table and can connect to it from a PHP script. Some records must be in this table for the example to fully function, so you should populate it like so:</p>
<pre>INSERT INTO users (email) VALUES (&#39;this@that.com&#39;)
INSERT INTO users (email) VALUES (&#39;me@school.edu&#39;)
INSERT INTO users (email) VALUES (&#39;fake@address.net&#39;)
</pre>
<p>After you&#39;ve created and populated the table, you can begin coding. As I mentioned, there are two scripts involved. To develop and test everything, you will:</p>
<ol class="bodycopy">
<li>Create the PHP script that queries the Oracle database.</li>
<li>Manually test the PHP script to see how it works.</li>
<li>Write the JavaScript code that interacts with the PHP script.</li>
<li>Make the HTML form that ties into the JavaScript.</li>
<li>Test the entire process.</li>
</ol>
<h2>Step 1: Programming the Database Query</h2>
<p>The entire PHP script is called ajax.php. (See <a class="bodylink" href="http://www.oracle.com/technology/pub/files/ullman-ajax-samples.zip">sample code zip</a>.) The purpose of this script is to run a query in Oracle and print a message based upon the results of the query. This is all very basic Oracle, SQL, and PHP, but I&#39;ll run through it to clarify what&#39;s happening.</p>
<p>The script expects to receive an email address in the URL, which is available in the variable $_GET[&#39;email&#39;]. Then the script connects to the Oracle database. The query itself counts how many records in the users table have that email address:</p>
<pre>SELECT COUNT(*) AS NUM_ROWS FROM users WHERE email=&#39;{$_GET[&#39;email&#39;]}&#39;
</pre>
<p>This should return either 0 or 1 (or nothing at all). The query is executed in Oracle and the results are bound to the PHP $rows variable. Now $rows represents how many records in the database have that email address. With respect to Ajax, the important part of the script is shown below.</p>
<pre>if ($rows &gt; 0) {
	echo &#39;Email address has already been registered!&#39;;
} else {
	echo &#39;Email address is available!&#39;;
}
</pre>
<p>One of those two messages will be printed, based upon the value of $rows. That&#39;s all there is to this page! Absolutely no HTML is needed as this script won&#39;t be used like a standard Web page.</p>
<p>If you want to improve this script, you can confirm that the email address matches a regular expression pattern for email addresses. If so, execute the query like normal; if not, echo a statement saying that it&#39;s not a valid email address. At the very least, you&#39;d likely want to prevent SQL injection attacks. (I.e., avoid using the $_GET variable in your query without some assurances of its validity.)</p>
<p>&nbsp;</p>
<h2>Step 2: Testing the PHP Script</h2>
<p>Because this entire Ajax process involves several technologies&mdash;HTML, JavaScript, PHP, SQL, and Oracle&mdash;you&#39;ll be best off testing each piece as you go. If you don&#39;t, you can easily get lost trying to find where a problem exists. Testing this PHP script should be fairly easy:</p>
<ol class="bodycopy">
<li>Make sure you&#39;ve created the table in Oracle and populated it.</li>
<li>Edit ajax.php so that it uses valid connection parameters for your Oracle installation.</li>
<li>Save the script as ajax.php.</li>
<li>Place it in the proper directory for your Web server.</li>
<li>Go to http://yoururl/ajax.php?email=X in your Web browser.</li>
</ol>
<p>In place of X, use one of the email addresses that&#39;s already in the database (e.g., http://yoururl/ajax.php?email=this@that.com). You should see the &quot;Email address has already been registered!&quot; message. Then use an email address that has not been stored. You should see the &quot;Email address is available!&quot; text. Once you have that working, you can move on to the actual Ajax aspect of this example.</p>
<p>&nbsp;</p>
<h2>Step 3: Programming the JavaScript</h2>
<p>This section of the process is probably the most difficult unless you&#39;ve done tons of JavaScript already. In any case, the JavaScript code is the heart of the Ajax process, as it performs and handles the request from the PHP page. I&#39;ll go through this code in detail.</p>
<p>The JavaScript code will define three functions. The first creates a request object variable:</p>
<pre>function createRequestObject() {
	var ro;
	if (navigator.appName == &quot;Microsoft Internet Explorer&quot;) {
		ro = new ActiveXObject(&quot;Microsoft.XMLHTTP&quot;);
	} else {
		ro = new XMLHttpRequest();
	}
	return ro;
}
</pre>
<p>You should be able to use this code for any Ajax application without modification. If the Web browser being used is Microsoft Internet Explorer, then the ro variable is initialized as an ActiveXObject of type Microsoft.XMLHTTP. For all other browsers, ro is a straightforward XMLHttpRequest type of object. That&#39;s really nothing else to it. The ro variable is now an object with all the necessary functionality to perform an asynchronous request. After creating the request object variable, this function then returns it.</p>
<p>This function is immediately called by the JavaScript code when the page is first loaded:</p>
<pre>var http = createRequestObject();
</pre>
<p>Now http represents the object and is globally available in the other functions.</p>
<p>Next up is the function that calls the PHP script:</p>
<pre>function sendRequest(email) {
	http.open(&#39;get&#39;, &#39;ajax.php?email=&#39; + encodeURIComponent(email));
	http.onreadystatechange = handleResponse;
	http.send(null);
}
</pre>
<p>This function takes one argument, which is the email address to be verified. Then it opens a connection to the PHP script. As the first argument in the open() method indicates, the request will be of type get, as opposed to post. The email address is appended to the URL, resulting in a page request like ajax.php?email=this@that.com. As you already know, this is how the PHP script is properly called. The encodeURIComponent() function just encodes the submitted value to make it URL-safe. The third line within the function tells the object what JavaScript function to call after the request has been made. The final line actually makes the request.</p>
<p>To recap, the first function, createRequestObject(), makes the object variable that&#39;s needed. The second function, sendRequest(), makes the actual request of the PHP script. Now we need a function that handles that request:</p>
<pre>function handleResponse() {
	if (http.readyState == 4) {
		document.getElementById(&#39;email_label&#39;).innerHTML = http.responseText;
	}
}
</pre>
<p>The previous function told the request object to call this function after the request has been made. This function first checks that the request was successfully made. If so, then http.readyState will be equal to 4. It could also be equal to 0 through 3 (you can search online for their actual meanings, if you care), but being equal to 4 means the request worked. The results of the request will be stored in the http.responseText attribute, the results being either of the two messages echoed by the PHP script. To be clear, if the request worked, then http.responseText will either be &quot;Email address has already been registered!&quot; or &quot;Email address is available!&quot;.</p>
<p>At this point, the JavaScript knows what the result of the PHP script was. All you need to do is get the JavaScript to notify the user of that result. An easy option would be to use an alert() box:</p>
<pre>alert(http.responseText);
</pre>
<p>If you put this within the conditional, you&#39;ll see the result when you test the whole application. (In fact, alert() is a great debugging tool to confirm what&#39;s going on in your JavaScript code.) However, I find these alerts to be annoying as an end user and would rather print the message next to the form&#39;s email input. To do so, I refer to the DOM (Document Object Model). Specifically, I assign the http.responseText to an element in my page called email_label. You&#39;ll see this in Step 4.</p>
<p>Whew! Hopefully you followed all that. To repeat, this is the most important and most complex part of this sequence. The JavaScript uses three functions to do three things: define a browser-specific request object; make a request to the PHP script; and, do something with the results of the request. If you&#39;re confused by any of this, or when it comes time to make a more complex Ajax application, you&#39;ll likely want to do a little research on JavaScript and DOM.</p>
<h2>Step 4: Making a JavaScript-functional HTML Form</h2>
<p>As I wrote previously, the premise behind this example is a registration form that takes, at the least, an email address. The minimal form would be:</p>
<pre>&lt;form action=&quot;somepage.php&quot; method=&quot;post&quot;&gt;
Email Address: &lt;input name=&quot;email&quot; type=&quot;text&quot; size=&quot;30&quot; maxlength=&quot;60&quot; /&gt;&lt;br /&gt;
First Name: &lt;input name=&quot;first_name&quot; type=&quot;text&quot; size=&quot;20&quot; maxlength=&quot;20&quot; /&gt;&lt;br /&gt;
(Rest of the form...)
&lt;/form&gt;
</pre>
<p>That&#39;s it! Well, not quite. You&#39;d want more inputs (a &quot;submit&quot; button, and so on). But for the purposes of this example, that&#39;s all you need. To avoid confusion, note that the action and method attributes here have nothing to do with the ajax.php script (which doesn&#39;t handle this form&#39;s submission).</p>
<p>Two more things are required, though. First, this form must somehow call the JavaScript code that makes the request. You could use several tricks to do that: make a clickable link, wait for the submit button to be clicked, and so on. I&#39;ve decided that once the user has entered an email address and has gone on to the next form element, the JavaScript should be called. This requires the onchange() method, which I&#39;ll add to the email input:</p>
<pre>Email Address: &lt;input name=&quot;email&quot; type=&quot;text&quot; size=&quot;30&quot; maxlength=&quot;60&quot;

onchange=&quot;sendRequest(this.form.email.value)&quot; /&gt;
</pre>
<p>As soon as the user changes the value in this input, including entering anything for the first time, and then tabs or clicks to another input, the JavaScript sendRequest() function will be called. It is fed one argument, the value of the email form input. If you look back at the JavaScript code, you can see how the value typed here gets sent to that function, which in turn is sent to the PHP script.</p>
<p>The second thing the HTML form needs is a place where JavaScript can &quot;write&quot; the received message so that it&#39;s visible to the user. As the handleResponse() function dictates, that message will be assigned to the innerHTML of an element called email_label. For this I add a span with that ID after the email address input:</p>
<pre>&lt;span id=&quot;email_label&quot;&gt;&lt;/span&gt;
</pre>
<p>That&#39;s it for tying the HTML form to the JavaScript. (See Listing 1 in the <a class="bodylink" href="http://www.oracle.com/technology/pub/files/ullman-ajax-samples.zip">sample code zip</a> for the final code.) Now it&#39;s time to see how it works!</p>
<h2>Step 5: Testing the Ajax Process</h2>
<p>Having completed the HTML and JavaScript, you should save that as ajax.html (or anything, really), then place it in the same directory as the PHP script. Load the form by running it in your Web browser, through a URL, not by opening it from the file system (i.e, go to http://yoururl/ajax.html). Type an unused email address in the proper input, then tab or click on the next input. Repeat using a stored email address.</p>
<p>The two screenshots below show the results you should see. Absolute magic!</p>
<p>&nbsp;</p>
<p><center><img alt="figure 1" src="http://www.oracle.com/technology/pub/images/ullman-ajax-f1.jpg" /></p>
<p><img alt="figure 2" src="http://www.oracle.com/technology/pub/images/ullman-ajax-f2.jpg" /></p>
<p></center></p>
<p>&nbsp;</p>
<h2>Conclusion</h2>
<p>In this article you&#39;ve seen how you can easily implement an Ajax process using PHP and Oracle. By doing so, server side validation is being accomplished within the client (although you should still keep server side validation as well, in case JavaScript is disabled). This particular example, in my opinion, demonstrates a nice feature for an end user, saving them from having to:</p>
<ul class="bodycopy" type="square">
<li>Submit the form</li>
<li>See that the email address has already been registered</li>
<li>Return to the form</li>
<li>Repeat</li>
</ul>
<p>Also, I think this example and the concept in general is kind of cool. And on the Web, that&#39;s not an insignificant factor.</p>
<p>Of course, there&#39;s tons more you can do with Ajax, PHP, and Oracle. The example in this article sent only one piece of data to the PHP script and retrieved just a string. You can send much more back and forth. If you have a significant amount of data to be returned to the calling page, you can have PHP return it in XML format, then use JavaScript to parse and display it. If you search online, you&#39;ll find many different examples.</p>
<p><strong><span class="boldbodycopy">Larry Ullman</span></strong></p>
]]></content:encoded>
			<wfw:commentRss>http://www.allfreetech.com/php/creating-an-ajax-process-using-php-and-oracle-113.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Password Hashing</title>
		<link>http://www.allfreetech.com/php/password-hashing-57.html</link>
		<comments>http://www.allfreetech.com/php/password-hashing-57.html#comments</comments>
		<pubDate>Sat, 23 Jan 2010 03:23:14 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[password]]></category>

		<guid isPermaLink="false">http://www.allfreetech.com/?p=57</guid>
		<description><![CDATA[In this article I&#39;m going to cover password hashing, a subject which is often poorly understood by newer developers. Recently I&#39;ve been asked to look at several web applications which all had the same security issue &#8211; user profiles stored in a database with plain text passwords. Password hashing is a way of encrypting a [...]]]></description>
			<content:encoded><![CDATA[<p>In this article I&#39;m going to cover password hashing, a subject which is often poorly understood by newer developers. Recently I&#39;ve been asked to look at several web applications which all had the same security issue &#8211; user profiles stored in a database with plain text passwords. Password hashing is a way of encrypting a password before it&#39;s stored so that if your database gets into the wrong hands, the damage is limited. Hashing is nothing new &#8211; it&#39;s been in use in Unix system password files since long before my time, and quite probably in other systems long before that. In this article I&#39;ll explain what a hash is, why you want to use them instead of storing real passwords in your applications, and give you some examples of how to implement password hashing in PHP and MySQL.<span id="more-57"></span></p>
<h2>Foreword</h2>
<p>As you read on you&#39;ll see that I advocate the use of a hashing algorithm called Secure Hashing Algorithm 1 (or SHA-1). Since I wrote this article, a team of researchers &#8211; Xiaoyun Wang, Yiqun Lisa Yin, and Hongbo Yu &#8211; have shown SHA-1 to be weaker than was previously thought. This means that for certain purposes such as digital signatures, stronger algorithms like SHA-256 and SHA-512 are now being recommended. For generating password hashes, SHA-1 still provides a more than adequate level of security for most applications today. You should be aware of this issue however and begin to think about using stronger algorithms in your code as they become more readily available.</p>
<p>For more information please see Bruce Schneier&#39;s analysis of the issue at <a href="http://www.schneier.com/blog/archives/2005/02/cryptanalysis_o.html">http://www.schneier.com/blog/archives/2005/02/cryptanalysis_o.html</a></p>
<h2>What Is A Hash?</h2>
<p>A hash (also called a hash code, digest, or message digest) can be thought of as the digital fingerprint of a piece of data. You can easily generate a fixed length hash for any text string using a one-way mathematical process. It is next to impossible to (efficiently) recover the original text from a hash alone. It is also vastly unlikely that any different text string will give you an identical hash &#8211; a &#39;hash collision&#39;. These properties make hashes ideally suited for storing your application&#39;s passwords. Why? Because although an attacker may compromise a part of your system and reveal your list of password hashes, they can&#39;t determine from the hashes alone what the real passwords are.</p>
<h2>So How Do I Authenticate Users?</h2>
<p>We&#39;ve established that it&#39;s incredibly difficult to recover the original password from a hash, so how will your application know if a user has entered the correct password or not? Quite simply &#8211; by generating a hash of the user-supplied password and comparing this &#39;fingerprint&#39; with the hash stored in your user profile, you&#39;ll know whether or not the passwords match. Let&#39;s look at an example:</p>
<h2>User Registration And Password Verification</h2>
<p>During the registration process our new user will provide their desired password (preferably with verification and through a secure session). Using code similar to the following, we store their username and password hash in our database:</p>
<p><img src="http://phpsec.org/articles/images/2005/password-hashing-01.png" /></p>
<p>Figure 1. Our user enters their preferred access details</p>
<pre class="code">&lt;?php

/* Store user details */

$passwordHash = sha1($_POST[&#39;password&#39;]);

$sql = &#39;INSERT INTO user (username,passwordHash) VALUES (?,?)&#39;;
$result = $db-&gt;query($sql, array($_POST[&#39;username&#39;], $passwordHash));

?&gt;</pre>
<p>The next time our user logs in, we check their access credentials using similar code as follows:</p>
<p><img src="http://phpsec.org/articles/images/2005/password-hashing-02.png" /></p>
<p>Figure 2. Logging back in</p>
<pre class="code">&lt;?php

/* Check user details */

$passwordHash = sha1($_POST[&#39;password&#39;]);

$sql = &#39;SELECT username FROM user WHERE username = ? AND passwordHash = ?&#39;;
$result = $db-&gt;query($sql, array($_POST[&#39;username&#39;], $passwordHash));
if ($result-&gt;numRows() &lt; 1)
{
    /* Access denied */
    echo &#39;Sorry, your username or password was incorrect!&#39;;
}
else
{
    /* Log user in */
    printf(&#39;Welcome back %s!&#39;, $_POST[&#39;username&#39;]);
}

?&gt;</pre>
<h2>Types Of Hashes</h2>
<p>There are a number of strong hashing algorithms in use, the most common of which are MD5 and SHA-1. Older systems &#8211; including many Linux variants &#8211; used Data Encryption Standard (DES) hashes. With only 56 bits this is no longer considered an acceptably strong hashing algorithm and should be avoided.</p>
<h3>Examples</h3>
<p>In PHP you can generate hashes using the <tt>md5()</tt> and <tt>sha1</tt> functions. <tt>md5()</tt> returns a 128-bit hash (32 hexadecimal characters), whereas <tt>sha1()</tt> returns a 160-bit hash (40 hexadecimal characters). For example:</p>
<pre class="code">&lt;?php

$string = &#39;PHP &amp; Information Security&#39;;
printf(&quot;Original string: %s\n&quot;, $string);
printf(&quot;MD5 hash: %s\n&quot;, md5($string));
printf(&quot;SHA-1 hash: %s\n&quot;, sha1($string));

?&gt;</pre>
<p>This code will output the following:</p>
<pre class="code">Original string: PHP &amp; Information Security
MD5 hash: 88dd8f282721af2c704e238e7f338c41
SHA-1 hash: b47210605096b9aa0129f88695e229ce309dd362</pre>
<p>In MySQL you can generate hashes internally using the <tt>password()</tt>, <tt>md5()</tt>, or <tt>sha1</tt> functions. <tt>password()</tt> is the function used for MySQL&#39;s own user authentication system. It returns a 16-byte string for MySQL versions prior to 4.1, and a 41-byte string (based on a double SHA-1 hash) for versions 4.1 and up. <tt>md5()</tt> is available from MySQL version 3.23.2 and <tt>sha1()</tt> was added later in 4.0.2.</p>
<pre class="code">mysql&gt; select PASSWORD( &#39;PHP &amp; Information Security&#39; );
+------------------------------------------+
| PASSWORD( &#39;PHP &amp; Information Security&#39; ) |
+------------------------------------------+
| 379693e271cd3bd6                         |
+------------------------------------------+
1 row in set (0.00 sec)

mysql&gt; select MD5( &#39;PHP &amp; Information Security&#39; );
+-------------------------------------+
| MD5( &#39;PHP &amp; Information Security&#39; ) |
+-------------------------------------+
| 88dd8f282721af2c704e238e7f338c41    |
+-------------------------------------+
1 row in set (0.01 sec)</pre>
<p><i>Note: Using MySQL&#39;s <tt>password()</tt> function in your own applications isn&#39;t recommended &#8211; the algorithm used has changed over time and prior to 4.1 was particularly weak.</i></p>
<p>You may decide to use MySQL to calculate your hash rather than PHP. The example of storing our user&#39;s registration details from the previous section then becomes:</p>
<pre class="code">&lt;?php

/* Store user details */

$sql = &#39;INSERT INTO user (username, passwordHash) VALUES (?, SHA1(?))&#39;;
$result = $db-&gt;query($sql, array($_POST[&#39;username&#39;], $_POST[&#39;password&#39;]));

?&gt;</pre>
<h2>Weaknesses</h2>
<p>As a security measure, storing only hashes of passwords in your database will ensure that an attacker&#39;s job is made that much more difficult. Let&#39;s look at the steps they&#39;ll now take in an effort to compromise your system. Assuming that they&#39;ve managed to access your user database and list of hashes, there&#39;s no way that they can then recover the original passwords to your system. Or is there?</p>
<p>The attacker will be able to look at your hashes and immediately know that any accounts with the same password hash must therefore also have the same password. Not such a problem if neither of the account passwords is known &#8211; or is it? A common technique employed to recover the original plain text from a hash is cracking, otherwise known as &#39;brute forcing&#39;. Using this methodology an attacker will generate hashes for numerous potential passwords (either generated randomly or from a source of potential words, for example a dictionary attack). The hashes generated are compared with those in your user database and any matches will reveal the password for the user in question.</p>
<p>Modern computer hardware can generate MD5 and SHA-1 hashes very quickly &#8211; in some cases at rates of thousands per second. Hashes can be generated for every word in an entire dictionary (possibly including alpha-numeric variants) well in advance of an attack. Whilst strong passwords and longer pass phrases provide a reasonable level of protection against such attacks, you cannot always guarantee that your users will be well informed about such practices. It&#39;s also less than ideal that the same password used on multiple accounts (or multiple systems for that matter) will reveal itself with an identical hash.</p>
<h2>Making It Better</h2>
<p>Both of these weaknesses in the hashing strategy can be overcome by making a small addition to our hashing algorithm. Before generating the hash we create a random string of characters of a predetermined length, and prepend this string to our plain text password. Provided the string (called a &quot;salt&quot;) is of sufficient length &#8211; and of course sufficiently random &#8211; the resulting hash will almost certainly be different each time we execute the function. Of course we must also store the salt we&#39;ve used in the database along with our hash but this is generally no more of an issue than extending the width of the field by a few characters.</p>
<p>When we validate a user&#39;s login credentials we follow the same process, only this time we use the salt from our database instead of generating a new random one. We add the user supplied password to it, run our hashing algorithm, then compare the result with the hash stored in that user&#39;s profile.</p>
<pre class="code">&lt;?php

define(&#39;SALT_LENGTH&#39;, 9);

function generateHash($plainText, $salt = null)
{
    if ($salt === null)
    {
        $salt = substr(md5(uniqid(rand(), true)), 0, SALT_LENGTH);
    }
    else
    {
        $salt = substr($salt, 0, SALT_LENGTH);
    }

    return $salt . sha1($salt . $plainText);
}

?&gt;</pre>
<p><i>Note: The function above is limited in that the maximum salt length is 32 characters. You may wish to write your own salt generator to overcome this limit and increase the entropy of the string.</i></p>
<p>Calling <tt>generateHash()</tt> with a single argument (the plain text password) will cause a random string to be generated and used for the salt. The resulting string consists of the salt followed by the SHA-1 hash &#8211; this is to be stored away in your database. When you&#39;re checking a user&#39;s login, the situation is slightly different in that you already know the salt you&#39;d like to use. The string stored in your database can be passed to <tt>generateHash()</tt> as the second argument when generating the hash of a user-supplied password for comparison.</p>
<p>Using a salt overcomes the issue of multiple accounts with the same password revealing themselves with identical hashes in your database. Although two passwords may be the same the salts will almost certainly be different, so the hashes will look nothing alike.</p>
<p>Dictionary attacks with pre-generated lists of hashes will be useless for the same reason &#8211; the attacker will now have to recalculate their entire dictionary for every individual account they&#39;re attempting to crack.</p>
<h2>Summary</h2>
<p>We&#39;ve seen now what hashes are and why you should store them instead of the plain text passwords they represent in your database. The examples above are a starting point and will get you on the right track with using hashes in your PHP applications. A little bit of work now may well mean much less of a headache further down the track!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.allfreetech.com/php/password-hashing-57.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Database-Based Authentication for PHP Apps, Part 2</title>
		<link>http://www.allfreetech.com/php/database-based-authentication-for-php-apps-part-2-52.html</link>
		<comments>http://www.allfreetech.com/php/database-based-authentication-for-php-apps-part-2-52.html#comments</comments>
		<pubDate>Sat, 23 Jan 2010 03:18:03 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Database]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://www.allfreetech.com/?p=52</guid>
		<description><![CDATA[In Part 2, you will expand on the example authentication and user entry solution constructed previously. Whereas the current solution treats all users as equals, the idea of users accessing all things equally is unusual in real applications. Rather, your users probably have roles and privileges limiting their access to data based on their position [...]]]></description>
			<content:encoded><![CDATA[<p>In Part 2, you will expand on the example authentication and user entry solution constructed previously. Whereas the current solution treats all users as equals, the idea of users accessing all things equally is unusual in real applications. Rather, your users probably have roles and privileges limiting their access to data based on their position of responsibility in a company.<span id="more-52"></span></p>
<p>Since Oracle8<em>i</em>, Oracle Database has included a security feature known as <a href="http://www.oracle.com/technology/deploy/security/database-security/index.html">Virtual Private Database</a> (VPD). A VPD lets you define, build and apply security policies. These security policies can restrict user access to specific tables or parts of tables. The policies let you manage access privileges for scheme by examining session level data, which the database creates when a user connects to an Oracle instance. VPD implementation requires a common repository schema that grants privileges to distinct scheme. Your user-defined database design must also provide a striping column in common repository tables. The access to these tables is then managed by the VPD policies.</p>
<p>VPD technology relies on the connection data stored in the database catalog. The connection data identifies and tracks activities of a schema (user). All but one field of this connection metadata is defined and managed by the Oracle database. The CLIENT_INFO column is available for developers to use when building user-defined applications. You can combine the session CLIENT_INFO column with a database column to stripe data at a granularity below schema and support multiple users in a single schema. Oracle E-Business 11i Suite stripes multiple organizations and currencies through views using the CLIENT_INFO column.</p>
<p>The combination of these technologies let you implement fine grained access privileges and roles through security policies. Alternatively, you can manage fine grained access by bundling the CLIENT_INFO column into your authentication model.</p>
<h2>Oracle Database Connection Architecture</h2>
<table align="right" bgcolor="#eeeeee" border="1" cellpadding="4" cellspacing="0" class="bodycopy" width="30%">
<tbody>
<tr>
<td colspan="3">
<h2>Sample Code Manifest</h2>
</td>
</tr>
<tr>
<td valign="middle" width="175"><strong>Program Name</strong></td>
<td valign="middle" width="96"><strong>Language</strong></td>
<td valign="middle" width="371"><strong>Description</strong></td>
</tr>
<tr>
<td valign="top">
<p>create_identity_db2.sql</p>
</td>
<td valign="top">
<p>SQL</p>
</td>
<td valign="top">
<p>Script that builds the database and seeds necessary rows to initialize the web application</p>
</td>
</tr>
<tr>
<td valign="top">
<p>credentials2.inc</p>
</td>
<td valign="top">
<p>PHP</p>
</td>
<td valign="top">
<p>Include file that defines the three global constants you use in all oci_connect() function calls</p>
</td>
</tr>
<tr>
<td valign="top">
<p>SignOnUserAdmin.php</p>
</td>
<td valign="top">
<p>PHP</p>
</td>
<td valign="top">
<p>Program file that resets the PHP session ID before calling the UserAdmin.php program</p>
</td>
</tr>
<tr>
<td valign="top">
<p>UserAdmin.php</p>
</td>
<td valign="top">
<p>PHP</p>
</td>
<td valign="top">
<p>Program file that (a) authenticates new users and returning active sessions; (b) renders a form to enter new users into an ACL when a user enjoys permissions; and (c) acts as a portal to view users based on access permissions.</p>
</td>
</tr>
<tr>
<td valign="top">
<p>UserView.php</p>
</td>
<td valign="top">
<p>PHP</p>
</td>
<td valign="top">
<p>Program file that authenticates new users and returning active sessions; and renders a form to view users based on access permissions</p>
</td>
</tr>
</tbody>
</table>
<p>The Oracle Database connection architecture has two facets that you should understand as a PHP programmer. First, the actual database connection is known as a database event and has a unique internally maintained numeric value. Every database connection creates a row of metadata in the system catalog. The row exists for the length of the connection and can be queried by any user with DBA role privileges from the V$SESSION view. The value of the SESSIONID in the database is unique and unrelated to the PHP session_id() function return value. Second, you have only one database SESSIONID value when you open a persistent connection with the oci_pconnect() function, while you can have more than one SESSIONID value when opening non-persistent connections.</p>
<p>The oci_connect() and oci_new_connect() functions create new SESSIONID values each time they run. Only the first oci_pconnect() function call creates a SESSIONID value but subsequent oci_pconnect() function calls reuse the open session. This reduces the dynamic marshalling expense required to build a new connection and enables preservation of state information between web pages and the database server. It is also possible for you to have two or more open non-persistent connections in the scope of a single PHP session or response-acknowledgment request cycle.</p>
<h2>Virtual Private Database Architecture</h2>
<p>The VPD architecture works by combining the connection metadata and user-defined security policies in a separate security schema. Security policies are procedural programming instructions written in PL/SQL stored functions. The policy functions have two formal parameters by default &mdash; one is a string representing an Oracle schema (also known as a user account), and the other is a table name. These policy functions return a variable length string that is appended to a WHERE clause for any SQL statement against the table. The WHERE clause lets you restrict the rows your users can query and transact against. You restrict access by filtering data on a qualifying column like an organization unit or security group role. Filtered tables are known as striped tables or views.</p>
<p>You implement a VPD by creating a schema/user, like SECURITY_ADMIN, and granting the schema access to the SYS.DBMS_RLS stored package. The DBMS_RLS package lets you add, maintain, and remove security policies. Then you can define policy stored functions in the SECURITY_ADMIN schema and add them as policies using the DBMS_RLS package.</p>
<p>The policies maintain a barrier around the data, which is typically located in another schema. The schema that owns the tables then grants access and transaction privileges to one or more other schemas. This configuration structure is similar to the default definer rights model for stored procedures in an Oracle database. Stored procedures act by default with the privileges of the schema where they are created.</p>
<p>In a classic definer rights model, the owning schema does not typically grant access and transaction rights to tables and views to other users. The schema grants only execute privileges on their stored procedures. The stored procedures guarantee that users access and transact against the data in a planned way. Unfortunately, the definer rights model does not enable slicing the data into roles based on the accessing source &mdash; user or schema.</p>
<p>Combining the policies of VPDs with the definer rights model lets you slice the data into pieces based on assigned roles and privileges. It does this because the tables are now accessed like subsets, by effectively creating views.</p>
<p>Figure 1 illustrates the idea that four users see different slices or views data.</p>
<p align="center"><img src="http://www.oracle.com/technology/pub/images/mclaughlin-phpid2-f1.gif" /></p>
<p>	<strong>Figure 1</strong> Definer Rights Model</p>
<p>Dividing data into views is done by adding a striping column to a table. You can map striping columns against a schema name in the VPD security policy function. You can also stripe the table on a column value other than database schema names.</p>
<p>Figure 2 illustrates how a numeric pseudo key column, SYSTEM_USER_GROUP_ID in the SYSTEM_USER table, lets you group users by a common value.</p>
<p align="center"><img src="http://www.oracle.com/technology/pub/images/mclaughlin-phpid2-f2.gif" /></p>
<p>	<strong>Figure 2</strong> A Striped Table</p>
<p>The 0 or 1 allows you to distinguish between and group by super and end users respectively. The SYSTEM_USER_GROUP_ID column values are pseudo keys, or numeric sequence values that typically map to descriptions found in another table. This striping model works well when users are not database scheme.</p>
<p>Schema-level security is a common approach that is consistent with traditional approaches to definer rights data models. This user level security approach lets you map non-scheme user accounts as striping column values. You can implement a VPD either way. The next two subsections describe the basic mechanics for both security approaches.</p>
<h3><em>Schema Level Security Model</em></h3>
<p>The schema level security model is straightforward to implement. It requires that all users are scheme. This is typically an undesirable business model and poses significant administrative burdens because of the expense associated with grants and synonym maintenance activities.</p>
<p>In this model, your policy functions secure the schema by querying the context information from the stored session metadata. Only the SYS_CONTEXT() function can secure the connection schema name. You call the SYS_CONTEXT() function inside your policy function as shown in the following sample pseudo code:</p>
<pre>CREATE OR REPLACE FUNCTION policy_user_maintenance
( schema           VARCHAR2
, tab              VARCHAR2 )
RETURN VARCHAR2 IS

  -- Define local variables.
  schema_name      VARCHAR2(30);
  where_clause     VARCHAR2(100);

BEGIN
  -- Get schema name.
  schema_name := SELECT SYS_CONTEXT(&#39;userenv &#39;,&#39;session_user &#39;) FROM dual;

  CASE schema_name
    WHEN user_a THEN
       ... qualifying logic ...
    WHEN user_b THEN
       ... qualifying logic ...
    WHEN user_c THEN
       ... qualifying logic ...
    ELSE
       ... qualifying logic ...
  END CASE;

  -- Return where clause.
  RETURN where_clause;

END policy_user_maintenance;
/
</pre>
<p>An implementation of a real POLICY_USER_MAINTENANCE() function like the example shell works only when users are scheme. Your PHP programs would require authentication, then each connect statement would become dynamic. This means that each PHP connection function call would need to have access to various scheme credentials. While this is an ugly solution, it is doable&#8211;albeit not likely a production solution that qualifies you for a bonus.</p>
<p>A better solution is possible. You achieve it by supplementing the context information stored in the session metadata. This is done by writing information to the CLIENT_INFO column in the V$SESSION view as discussed in the next subsection. The CLIENT_INFO column is a 64 character user-defined string in the session metadata.</p>
<h3><em>User Level Security Model</em></h3>
<p>The user level security model requires more effort to implement than the schema level model. You implement user level security by a three step process. You (a) authenticate credentials against your ACL, (b) write your authenticated credentials to the context information stored in the session metadata, and (c) write your profile stored functions to validate against the user defined context information stored in the session metadata.</p>
<p><strong>Authenticating Credentials. </strong>While you have discretion as to how you implement your authentication, you should keep it simple. Assuming that you have adopted a similar SYSTEM_USER table as your ACL, you can authenticate against the user name and password in the table. Then, you can get a copy of the primary key column value, and use the primary key value as your context identifier in the session metadata. This eliminates upper, lower and mixed case validations but it can also appear as a magic number solution.</p>
<h4>Reading and Writing Credentials to Session Metadata</h4>
<p>The process of writing to and reading from the session CLIENT_INFO column requires you to use the DBMS_APPLICATION_INFO package. You use the SET_CLIENT_INFO procedure in the DBMS_APPLICATION_INFO package to write data into the 64 character CLIENT_INFO column found in the V$SESSION view. The following anonymous PL/SQL block assumes that the SYSTEM_USER_ID column value is 1:</p>
<pre>BEGIN
   -- Write value to V$SESSION.CLIENT_INFO column.
   DBMS_APPLICATION_INFO.SET_CLIENT_INFO(&#39;1 &#39;);
END;
/
</pre>
<p>You can now read this value by calling the READ_CLIENT_INFO() procedure. You should enable SERVEROUTPUT using SQL*Plus to see the rendered output when you run the following program:</p>
<pre>DECLARE
  client_info      VARCHAR2(64);
BEGIN
   -- Read value from V$SESSION.CLIENT_INTO column.
   DBMS_APPLICATION_INFO.READ_CLIENT_INFO(client_info);

   -- Print it to console.
   DBMS_OUTPUT.PUT_LINE(&#39;[ &#39;||client_info||&#39;]&#39;);
END;
/
</pre>
<p>User-defined session columns let you store unique information related to user credentials from your ACL. You assign a session column value during user authentication. Then, the session CLIENT_INFO column allows you to manage multiple user interactions in a single schema. Authenticated users can access whole or partial rows from tables when their session CLIENT_INFO column value matches a striping column values in the table.</p>
<p><strong>Writing a Security Profile Stored Procedure. </strong>Your policy functions secure the user-defined CLIENT_INFO column value by querying the context information from the stored session metadata. Both the USERENV() and SYS_CONTEXT() functions can secure the user-defined column value.</p>
<p>You can use the USERENV() or SYS_CONTEXT() function to query database session metadata. You can also use these functions when you are unsure if your user account has the DBA role privilege. The SYS_CONTEXT() function is more flexible and provides greater access to the session metadata, and you should use it in lieu of the older USERENV() function. The following demonstrates how you check whether you have the DBA role by using the SYS_CONTEXT() function:</p>
<pre>SELECT   sys_context(&#39;userenv &#39;, &#39;isdba &#39;)
FROM     dual;
</pre>
<p>The query will return a Boolean true when you have the DBA role and false when you do not have the role. The SYS_CONTEXT() function provides an alternative to querying the V$SESSION view for connection information, and is available to restricted permission scheme.</p>
<p>You change the previously discussed policy function by replacing the SCHEMA_NAME variable with the CLIENT_INFO variable. Then, you use the SYS_CONTEXT function to select the current CLIENT_INFO column value from the session metadata. All remaining logic in the security policy procedure would also require changes from the schema name to the user-defined CLIENT_INFO column value. The following shows a revised pseudo-code security policy function for a user-level security management procedure:</p>
<pre>CREATE OR REPLACE FUNCTION policy_user_maintenance
( client_info      VARCHAR2
, tab              VARCHAR2 )
RETURN VARCHAR2 IS

  -- Define local variables.
  client_info_name VARCHAR2(64);
  where_clause     VARCHAR2(100);

BEGIN
  -- Get schema name.
  client_info_name := SELECT SYS_CONTEXT(&#39;userenv &#39;,&#39;client_info &#39;) FROM dual;

  CASE client_info_name
    WHEN user_a THEN
       ... qualifying logic ...
    WHEN user_b THEN
       ... qualifying logic ...
    WHEN user_c THEN
       ... qualifying logic ...
    ELSE
       ... qualifying logic ...
  END CASE;

  -- Return where clause.
  RETURN where_clause;

END policy_user_maintenance;
/
</pre>
<h2>Authentication Process Model</h2>
<p>This section provides an authentication solution that uses the CLIENT_INFO session column and striped tables. Rather then introduce the complexity of setting up a security administration schema and policy function, the solution uses a striped view. Striped views contain WHERE clauses that join user session data values to the striping column values. This approach mimics the dynamic WHERE clauses built by VPD security functions. Oracle E-Business Suite uses this technique to access dynamic views containing user specific organization and reporting currency data.</p>
<p>The authentication process model depends on the same two tables introduced in Part 1. These tables are shown in Figure 3.</p>
<p align="center"><img src="http://www.oracle.com/technology/pub/images/mclaughlin-phpid2-f3.gif" /></p>
<p>	<strong>Figure 3</strong> Basic Authentication Data Model</p>
<p>While there is no change in the definition of the tables, this authentication model implements programming logic that takes advantage of data striping provided by the SYSTEM_USER_GROUP_ID column in the SYSTEM_USER table.</p>
<p>The authentication model stripes only for super and end users, which are the administration and employee groups respectively. You can identify super users by querying the database and checking the SYSTEM_USER_GROUP_ID column for a value of one. End users will have a value greater than or equal to 1. The PHP scripts read the SYSTEM_USER_GROUP_ID to set the form session as a super or end user; and they set the CLIENT_INFO column values using the DBMS_APPLICTION_INFO package to read data from striped views.</p>
<p>The balance of this section discusses how to setup and experiment with the test application before analyzing the code and identify management solution. The <u>sample schema and code</u> differ from that in Part 1 and they use a different database schema. This enables you to have both sample code trees up and running on your system for comparison purposes.</p>
<p><strong>Setup Test Application. </strong>The demonstration application uses an Oracle schema named IDMGMT2, and the password is the same as the schema. You can create the user and environment by following these steps:</p>
<p>1. Log into the database as the privileged SYSTEM user and run the following commands. You must explicitly grant the CREATE ALL VIEWS permission when using Oracle 10g due to changes in how the RESOURCE role works:</p>
<pre>SQL&gt; CREATE USER IDMGMT2 IDENTIFIED BY IDMGMT2;
SQL&gt; GRANT CONNECT, RESOURCE TO IDMGMT2;
SQL&gt; GRANT CREATE ALL VIEWS TO IDMGMT2;
</pre>
<p>2. Connect to the new user schema:</p>
<pre>SQL&gt; CONNECT IDMGMT2/IDMGMT2@XE</pre>
<p>3. Run the create_identity_db2.sql script in the IDMGMT2 schema to create all necessary objects and seed the SYSTEM_USER table:</p>
<pre>SQL&gt; @create_identity_db2.sql</pre>
<p>4. Place the following files in your htdocs directory or a subdirectory of the htdocs directory:</p>
<pre> &bull; SignOnUserAdmin.php
 &bull; UserAdmin.php
 &bull; UserView.php
</pre>
<p>These steps complete the setup necessary for our purposes here. You can now experiment with either the realm or session identification management examples.</p>
<p><strong>Experiment with Session Authentication. </strong>You can experiment with the realm identity management by following these steps:</p>
<p>1. Open the realm identification management examples by using the following URL:</p>
<pre>http://localhost/SignOnUserAdmin.php</pre>
<p>2. You can use either of the following seeded accounts as valid credentials using Basic HTTP/HTTPS authentication. Make sure <em>you don&#39;t have the cap lock enabled</em> because they are case sensitive.</p>
<table align="center" border="1" cellpadding="4" cellspacing="0" class="bodycopy">
<tbody>
<tr>
<td bgcolor="#eeeeee" valign="top" width="156"><strong>User Name</strong></td>
<td bgcolor="#eeeeee" valign="top" width="147"><strong>Password</strong></td>
</tr>
<tr>
<td valign="top">administrator</td>
<td valign="top">welcome</td>
</tr>
<tr>
<td valign="top">guest</td>
<td valign="top">guest</td>
</tr>
</tbody>
</table>
<p align="center"><img src="http://www.oracle.com/technology/pub/images/mclaughlin-phpid2-f4.gif" /></p>
<p>	<strong>Figure 4 </strong>Cookie and Session Login page</p>
<p>You will be presented with the following page, where you should login as a privileged user in this test system, using the <em>&quot;administrator&quot;</em> user name and <em>&quot;welcome&quot;</em> password credentials, and not the restricted privilege &quot;guest&quot; user.</p>
<p>After authenticating your credentials, the program transfers you to the <strong>New User</strong> (UserAdmin.php) form. You should notice that there are changes from the prior <strong>New User</strong> (AddDBUser.php) form. The form now supports entering new users as belonging to either the administration or employee group, and the form provides a view to see entered users. The administration group members have privileges to add new users while the employee group&#39;s privileges are restricted and unable to add new users.</p>
<p>3. You can now enter a user in both the administration or employee groups. The administration group is a super user group and the employee group is for end users. You should enter both of the following users:</p>
<table align="center" border="1" cellpadding="4" cellspacing="0" class="bodycopy">
<tbody>
<tr>
<td bgcolor="#eeeeee" valign="top" width="108">&nbsp;</td>
<td bgcolor="#eeeeee" valign="top" width="192"><strong>Administration Group</strong></td>
<td bgcolor="#eeeeee" valign="top" width="195"><strong>Employee Group</strong></td>
</tr>
<tr>
<td valign="top">
<p><strong>User ID</strong></p>
</td>
<td valign="top">
<p>jonesi</p>
</td>
<td valign="top">
<p>ravenwoodm</p>
</td>
</tr>
<tr>
<td valign="top">
<p><strong>Password</strong></p>
</td>
<td valign="top">
<p>jonesi</p>
</td>
<td valign="top">
<p>ravenwoodm</p>
</td>
</tr>
<tr>
<td valign="top">
<p><strong>First Name</strong></p>
</td>
<td valign="top">
<p>Indiana</p>
</td>
<td valign="top">
<p>Marion</p>
</td>
</tr>
<tr>
<td valign="top">
<p><strong>Last Name</strong></p>
</td>
<td valign="top">
<p>Jones</p>
</td>
<td valign="top">
<p>Ravenwood</p>
</td>
</tr>
</tbody>
</table>
<p>Make sure you don&#39;t have the cap lock enabled because entries are case sensitive. If the <strong>Administration Group</strong> radio button is not clicked, you should click it when entering Indiana Jones. Click the <strong>Employee Group</strong> radio button before entering Marion Ravenwood. You will use both accounts in subsequent navigation steps.</p>
<p align="center"><img src="http://www.oracle.com/technology/pub/images/mclaughlin-phpid2-f5.gif" /></p>
<p>	<strong>Figure 5 </strong>Cookie and Session Login Page</p>
<p>The administrative group is the default for the <strong>New User</strong> (UserAdmin.php) form. When you create an administrative group user, the SYSTEM_USER_GROUP_ID column gets a 0 value. Creating a new user in the employee group inserts a value of 1 in the same column. Users who are defined in the administration group have the privilege to create other users, while those in the employee group cannot create new users.</p>
<p>After you enter Indiana Jones and Marion Ravenwood, you will see the <strong>New User</strong> form, as shown in Figure 6. This form acknowledges that you&#39;ve entered a new user. If your credentials fail to meet set conditions the same line would explain why.</p>
<p align="center"><img src="http://www.oracle.com/technology/pub/images/mclaughlin-phpid2-f6.gif" /></p>
<p>	<strong>Figure 6</strong> Main Administration page with a confirmation message</p>
<p>4. After you enter both Indiana Jones and Marion Ravenwood as the &quot;administrator&quot; user, you can view your user ACL members by clicking the View User button.</p>
<p align="center"><img src="http://www.oracle.com/technology/pub/images/mclaughlin-phpid2-f7.gif" /></p>
<p>	<strong>Figure 7 </strong>User Account View from an Administration Group user</p>
<p>4. After you have confirmed entry Indiana Jones as a system administrator and Marion Ravenwood as a system user, you should click the <strong>Log Out</strong> button. Then, you should log in as a system user by using the guest or Marion Ravenwood credentials. Figure 8 shows how the <strong>New User</strong> form looks to an employee group user.</p>
<p align="center"><img src="http://www.oracle.com/technology/pub/images/mclaughlin-phpid2-f8.gif" /></p>
<p>	<strong>Figure 8</strong> Main Administration page for a Restricted User Account</p>
<p>5. When you click the <strong>View User</strong> button as Marion Ravenwood, only your account is displayed. This behavior works because the form returns a striped view of data.</p>
<p align="center"><img src="http://www.oracle.com/technology/pub/images/mclaughlin-phpid2-f9.gif" /></p>
<p>	<strong>Figure 9</strong> User Account View from a Employee Group User</p>
<p>You have now experimented with the striped user authentication forms. The next section analyzes how identity management works to support these forms.</p>
<p><strong>Analyze the UserAdmin.php Session Authentication Code. </strong>Several changes to prior version of the AddDBUser.php program presented in the earlier paper make this striped behavior possible. The AddDBUser.php program has been changed to the UserAdmin.php script with the following changes:</p>
<ul>
<li>Modify the get_session() function to get and set proper user-defined session metadata variable values.</li>
<li>Modify the create_new_db_user() function to verify whether the current authenticated user is a privileged user or not.</li>
<li>Modify the get_message() function and the base form logic to provide confirmation of user entry.</li>
<li>Disable the text entry fields, <strong>Add User</strong> button and <strong>Administration/Employee</strong> group radio buttons unless the user is a privileged user.</li>
<li>The base code of the verify_db_login() function now captures the group value and assigns it to a $_SESSION variable.</li>
</ul>
<p>You will build striped web applications by implementing similar coding changes in your applications. Each of the five business logic changes are examined individually.</p>
<p><strong>Modify the get_session() Function. </strong>The get_session() function has changed between the AddDbUser.php and UserAdmin.php versions to enable separate request handling based on a user&#39;s group assignment. The select statement now returns two additional columns &mdash; they are the SYSTEM_USER_ID and SYSTEM_USER_GROUP_ID values. The get_session() function evaluates whether the SYSTEM_USER_GROUP_ID value is 0, which is the administration group in this application. When the user has administration privileges, the function assigns the group value to the $_SESSION[&#39;client_info&#39;] value. When the user does not have administration privileges, the function assigns the individual&#39;s record identifier as the $_SESSION[&#39;client_info&#39;] value.</p>
<p>You can then use the $_SESSION[&#39;client_info&#39;] value to control future connections in the scope of the currently running script file. You maintain a $_SESSION[&#39;client_info&#39;] value in your PHP program scope when you&#39;re using non-persistent connections to the Oracle database. You can avoid this by simply setting the CLIENT_INFO column value in the session metadata, provided you made all persistent connections &mdash; using oci_pconnect() function.</p>
<p>The modified get_session() function is shown below with the changes highlighted by bold text:</p>
<pre>function get_session($sessionid,$userid = null,$passwd = null)
{
  // Attempt connection and evaluate password.
  if ($c = @oci_connect(SCHEMA,PASSWD,TNS_ID))
  {
    // Assign metadata to local variable.
    $remote_address = $_SERVER[&#39;REMOTE_ADDR&#39;];

    // Return database UID within 5 minutes of session registration.
    $s = oci_parse($c,&quot;SELECT   su.system_user_id
                       ,        su.system_user_name
                       ,        su.system_user_group_id
                       ,        ss.system_remote_address
                       ,        ss.system_session_id
                       FROM     system_user su JOIN system_session ss
                       ON       su.system_user_id = ss.system_user_id
                       WHERE    ss.system_session_number = :sessionid
                       AND     (SYSDATE - ss.last_update_date) &lt;=
                                  .003472222&quot;);

    // Bind the variables as strings.
    oci_bind_by_name($s,&quot;:sessionid&quot;,$sessionid);

    // Execute the query and raise missing table message on failure.
    if (@oci_execute($s,OCI_DEFAULT))
    {
      // Check for a validated user, also known as a fetched row.
      if (oci_fetch($s))
      {
        // Assign unqualified values.
        $_SESSION[&#39;userid&#39;] = oci_result($s,&#39;SYSTEM_USER_NAME&#39;);

        // Assign the privileged group or user primary key column value.
        if (oci_result($s,&#39;SYSTEM_USER_GROUP_ID&#39;) == 0)
          $_SESSION[&#39;client_info&#39;] = oci_result($s,&#39;SYSTEM_USER_GROUP_ID&#39;);
        else
          $_SESSION[&#39;client_info&#39;] = oci_result($s,&#39;SYSTEM_USER_ID&#39;);

        // Check for same remote address.
        if ($remote_address == oci_result($s,&#39;SYSTEM_REMOTE_ADDRESS&#39;))
        {
          // Refresh last update timestamp of session.
          update_session($c,$sessionid,$remote_address);
          return (int) oci_result($s,&#39;SYSTEM_SESSION_ID&#39;);
        }
        else
        {
          // Log attempted entry.
          record_session($c,$sessionid);
          return 0;
        }
      }
      else
      {
        // Record when not first login.
        if (!isset($userid) &amp;&amp; !isset($passwd))
          record_session($c,$sessionid);
        return 0;
      }
    }
    else
    {
      // Set error message.
      set_error(__FUNCTION__,array(&#39;SYSTEM_USER&#39;,&#39;SYSTEM_SESSION&#39;));
    }

    // Close the connection.
    oci_close($c);
  }
  else
  {
    $errorMessage = oci_error();
    print htmlentities($errorMessage[&#39;message&#39;]).&quot;&lt;br /&gt;&quot;;
  }
}
</pre>
<p><strong>Modify the create_new_db_user() Function. </strong>The create_new_db_user() function now returns a Boolean variable as opposed to a void. This change lets the UserAdmin.php program call the function inside a control structure, as shown below:</p>
<pre>if (create_new_db_user($_SESSION[&#39;db_userid&#39;]
                      ,$newuserid
                      ,$newpasswd
                      ,$fname
                      ,$lname
                      ,$usergroup))
{
  // Set code to successful.
  $code = USER_VALID;

  // Render new form with successful acknowledgement.
  userAdminForm(array(&quot;code&quot;=&gt;$code
                     ,&quot;form&quot;=&gt;&quot;UserAdmin.php&quot;
                     ,&quot;userid&quot;=&gt;$newuserid));
}
</pre>
<p>The change lets you append a success message to the next rendered copy of the <strong>New User</strong> form. The function also guards against an unauthorized user adding a new user. While the functionality to add a new user is restricted by conditional rendering of the form based on the privileges of authenticated users, it should also be protected in the function. The precautions create a good design model that does not exclusively rely on tightly coupled behaviors. Functional coupling can get lost during maintenance coding and you should ensure independence of action for all functions. Functions can then be used by different user interfaces (UIs).</p>
<p>This application UI takes the approach to prevent an unauthorized attempt to create a new user, but another may enable the attempt but report back to the user that they are unauthorized. Functions supporting UIs should be flexible enough to handle both approaches, like this one.</p>
<p>The create_new_db_user() function now sets a $written control variable to false on entry. Then, it checks whether the active user is authorized to add a new user. It does this by reading the $_SESSION[&#39;client_info&#39;] value set by the get_session() function. The control variable is reset to true when a new user is successfully added to the database. The complete function follows below:</p>
<pre>// Add a new user to the authorized control list.
function create_new_db_user($userid,$nuserid,$npasswd,$fname,$lname
                           ,$ugroup)
{
  // Set control variable.
  $written = false;

  // Attempt connection and evaluate password.
  if ($c = @oci_connect(SCHEMA,PASSWD,TNS_ID))
  {
    // Check for prior insert, possible on web page refresh.
    if (!is_inserted($c,$nuserid) &amp;&amp; ($_SESSION[&#39;client_info&#39;] == 0))
    {
      // Return database UID.
      $s = oci_parse($c,&quot;INSERT INTO system_user
                         ( system_user_id
                         , system_user_name
                         , system_user_password
                         , first_name
                         , last_name
                         , system_user_group_id
                         , system_user_type
                         , start_date
                         , created_by
                         , creation_date
                         , last_updated_by
                         , last_update_date )
                         VALUES
                         ( system_user_s1.nextval
                         , :newuserid
                         , :newpasswd
                         , :firstname
                         , :lastname
                         , :usergroup
                         , 1
                         , SYSDATE
                         , :userid1
                         , SYSDATE
                         , :userid2
                         , SYSDATE)&quot;);

      // Bind the variables as strings.
      oci_bind_by_name($s,&quot;:newuserid&quot;,$nuserid);
      oci_bind_by_name($s,&quot;:newpasswd&quot;,sha1($npasswd));
      oci_bind_by_name($s,&quot;:firstname&quot;,$fname);
      oci_bind_by_name($s,&quot;:lastname&quot;,$lname);
      oci_bind_by_name($s,&quot;:usergroup&quot;,$ugroup);
      oci_bind_by_name($s,&quot;:userid1&quot;,$userid);
      oci_bind_by_name($s,&quot;:userid2&quot;,$userid);

      // Execute the query print error handling for missing table.
      if (@oci_execute($s,OCI_COMMIT_ON_SUCCESS))
      {
        // Update control variable for insert.
        $written = true;
      }
      else
      {
        // Set error message.
        set_error(__FUNCTION__,array(&#39;SYSTEM_USER&#39;));
      }
    }

    // Close the connection.
    oci_close($c);

    // Return control variable.
    return $written;
  }
  else
  {
    $errorMessage = oci_error();
    print htmlentities($errorMessage[&#39;message&#39;]).&quot;&lt;br /&gt;&quot;;
  }
}
</pre>
<p><strong>Modify the get_message() Function. </strong>The only change to the get_message() function is the addition of two criteria. One acknowledges successful entry of a new user, and the other supports an unauthorized access message. You should also note that the previous magic numbers have been converted to constants, which improves the readability of the code.</p>
<p><strong>Disable Conditionally the Add User and Group Radio Buttons. </strong>The userAdminForm() function now conditionally renders the text entry fields, <strong>Add User</strong> and <strong>Administration/Employee</strong> group radio buttons. This change implements the UI discussed earlier and prevents unauthorized attempts to add new users. While the text entry fields and <strong>Administration/Employee</strong> group radio buttons have no utility when the submission button is disabled, they are likewise disabled to avoid end-user confusion. They are disabled when the following conditional statement is not met:</p>
<pre>if ((!@$_SESSION[&#39;client_info&#39;]) &amp;&amp; (@$_SESSION[&#39;client_info&#39;] == 0))</pre>
<p>The statement checks whether the session level variable is both set and equal to 0, which is the only authorized or privileged account in the application. In a real implementation, you would probably have a list of authorized account privileges in an array. Assuming that you initialize a privileged ACL in a $authorized_list array variable, you would most likely use a conditional expression like this:</p>
<pre>if ((!@$_SESSION[&#39;client_info&#39;]) &amp;&amp;
    (array_search(@$_SESSION[&#39;client_info&#39;],$authorized_list)))
</pre>
<p><strong>Modify the verify_db_login() Function. </strong>The application design enables a session to be updated when a user log out and back in using the same credentials within a timeout window. This design requires you to update the behavior of the verify_db_login() function. You change the function by enabling it to capture and set the $_SESSION[&#39;client_info&#39;] value, as shown below in the function implementation:</p>
<pre>function verify_db_login($userid,$passwd)
{
  // Attempt connection and evaluate password.
  if ($c = @oci_connect(SCHEMA,PASSWD,TNS_ID))
  {
    // Return database UID.
    $s = oci_parse($c,&quot;SELECT   system_user_id
                       ,        system_user_group_id
                       FROM     system_user
                       WHERE    system_user_name = :userid
                       AND      system_user_password = :passwd
                       AND      SYSDATE BETWEEN start_date
                                        AND NVL(end_date,SYSDATE)&quot;);

    // Assign encryted value to variable, avoiding E_STRICT error.
    $newpassword = sha1($passwd);

    // Bind the variables as strings.
    oci_bind_by_name($s,&quot;:userid&quot;,$userid);
    oci_bind_by_name($s,&quot;:passwd&quot;,$newpassword);

    // Execute the query and raise missing table message on failure.
    if (@oci_execute($s,OCI_DEFAULT))
    {
      // Check for a validated user, also known as a fetched row.
      if (oci_fetch($s))
      {
        // Confirm session and collect foreign key reference column.
        if ((!isset($_SESSION[&#39;session_id&#39;])) ||
            (!isset_sessionid($c,$_SESSION[&#39;sessionid&#39;])))
        {
          $_SESSION[&#39;db_userid&#39;] = oci_result($s,&#39;SYSTEM_USER_ID&#39;);
          $_SESSION[&#39;client_info&#39;] = oci_result($s,&#39;SYSTEM_USER_GROUP_ID&#39;);
          register_session($c,(int) $_SESSION[&#39;db_userid&#39;]
                          ,$_SESSION[&#39;sessionid&#39;]);
        }

        // User verified.
        return true;
      }
      else
      {
        // User not verified.
        return false;
      }
    }
    else
    {
      // Set error message.
      set_error(__FUNCTION__,array(&#39;SYSTEM_USER&#39;));
    }

    // Close the connection.
    oci_close($c);
  }
  else
  {
    $errorMessage = oci_error();
    print htmlentities($errorMessage[&#39;message&#39;]).&quot;&lt;br /&gt;&quot;;
  }
}
</pre>
<p>The five analyses have reviewed the changes to UserAdmin.php script. The next section will examine the new UserView.php program.</p>
<p><strong>Analyze the UserView.php Session Authentication Code. </strong>The UserView.php program is new but is an outgrowth of the business logic found in the UserAdmin.php program. The development of the new form required three new functions and allowed the removal of several functions. It also required the development of a striped AUTHORIZED_USER view in the database.</p>
<p>By synchronizing the functions, you are positioned to remove them from the individual script files and place them in a single shared library to support the application. This is the next recommended step in building your own identity management solution.</p>
<p>The three new functions added are:</p>
<ul>
<li>The return_users() function that gets the results from a striped view.</li>
<li>The set_client_info() function that sets a user-defined session metadata value in the database.</li>
<li>The strip_special_characters() function that lets you use otherwise unsupported formatting characters in your PL/SQL program statements, like line returns.</li>
</ul>
<p>Before examining how the PHP code in the UserView.php form implements the architecture and design, you should first understand how the striped view works. The following is the view definition provided in the create_identity_db2.sql script:</p>
<pre>CREATE OR REPLACE VIEW authorized_user AS
  SELECT   su.system_user_id AS user_id
  ,        su.system_user_name AS user_name
  ,        cl.common_lookup_meaning AS user_privilege
  ,        CASE
             WHEN su.first_name IS NOT NULL AND  su.last_name IS NOT NULL
             THEN su.first_name||&#39; &#39;|| su.last_name
             ELSE NULL
           END AS employee_name
  ,        su.system_user_group_id AS group_id
  FROM     system_user su JOIN common_lookup cl
  ON       su.system_user_group_id = cl.common_lookup_id
  WHERE    TO_NUMBER(NVL(SYS_CONTEXT(&#39;userenv&#39;,&#39;client_info&#39;),-1)) = 0
  OR       su.system_user_id =
             TO_NUMBER(NVL(SYS_CONTEXT(&#39;userenv&#39;,&#39;client_info&#39;),-1))
  ORDER BY CASE
             WHEN first_name IS NULL
             THEN 0
             ELSE 1
           END
  ,        su.last_name
  ,        su.first_name
  ,        su.system_user_id;
</pre>
<p>The WHERE clause requires resolution of the call to the SYS_CONTEXT() function. When the function fails to return a value, the null value substitution is -1, which should return no rows by design. Therefore, this striped view only works when the user-defined database session metadata CLIENT_INFO column value is set to a matching value by the web application.</p>
<p><strong>The return_users() Function.</strong>The return_users() function gets the results from a striped view and formats them for display. The function calls the set_client_info() function before querying the data. The set_client_info() function sets the server session CLIENT_INFO value by calling the DBMS_APPLICATION_INFO() procedure. This limits the view to seeing only those records consistent with the end-user permissions.</p>
<p>The view returns information about all ACL users when the active user is in the <strong>Administration</strong> group, and only information about the active user when the active user is in the <strong>Employee</strong> group. This is clearly a limitation of trying to keep the example short and an opportunity for improvement when you implement your own identity management system. The complete return_user() function follows for your reference:</p>
<pre>function return_users($userid)
{
  // Attempt connection and evaluate password.
  if ($c = @oci_connect(SCHEMA,PASSWD,TNS_ID))
  {
    // Set the connection striping.
    set_client_info($c,$userid);

    // Return database UID.
    $s = oci_parse($c,&quot;SELECT   user_id
                       ,        user_name
                       ,        user_privilege
                       ,        employee_name
                       FROM     authorized_user&quot;);

    // Execute the query, error handling should be added.
    if (@oci_execute($s,OCI_DEFAULT))
    {
      // Set a first row control variable.
      $no_row_fetched = true;

      // Initialize the return variable in case no rows are found.
      $out = &#39;&#39;;

      // Check for a validated user, also known as a fetched row.
      while (oci_fetch($s))
      {
        if ($no_row_fetched)
        {
          $out .= &#39;&lt;tr&gt;&#39;;
          $out .= &#39;&lt;td align=&quot;center&quot;&gt;&lt;b&gt;User ID&lt;/b&gt;&lt;/td&gt;&#39;;
          $out .= &#39;&lt;td align=&quot;center&quot;&gt;&lt;b&gt;User Name&lt;/b&gt;&lt;/td&gt;&#39;;
          $out .= &#39;&lt;td align=&quot;center&quot;&gt;&lt;b&gt;User Privilege&lt;/b&gt;&lt;/td&gt;&#39;;
          $out .= &#39;&lt;td align=&quot;center&quot;&gt;&lt;b&gt;Employee Name&lt;/b&gt;&lt;/td&gt;&#39;;
          $out .= &#39;&lt;/tr&gt;&#39;;
          $no_row_fetched = false;
        }

        $out .= &#39;&lt;tr&gt;&#39;;

        for ($i = 1;$i &lt;= oci_num_fields($s);$i++)
          if (!is_null(oci_result($s,$i)))
          {
            if ($i == 1)
              $out .= &#39;&lt;td align=&quot;right&quot;&gt;&#39;.oci_result($s,$i).&#39;&lt;/td&gt;&#39;;
            else
              $out .= &#39;&lt;td&gt;&#39;.oci_result($s,$i).&#39;&lt;/td&gt;&#39;;
          }
          else
            $out .= &#39;&lt;td&gt;&nbsp;&lt;/td&gt;&#39;;
        $out .= &#39;&lt;/tr&gt;&#39;;
      }
    }
    else
    {
      // Set error message.
      set_error(__FUNCTION__,array(&#39;AUTHORIZED_USER&#39;));
    }

    // Close the connection.
    oci_close($c);

    // Return formatted string.
    return $out;
  }
  else
  {
    $errorMessage = oci_error();
    print htmlentities($errorMessage[&#39;message&#39;]).&quot;&lt;br /&gt;&quot;;
  }
}
</pre>
<p><strong>The set_client_info() Function.</strong>The set_client_info() function calls a PL/SQL procedure inside an anonymous PL/SQL block.</p>
<pre>// Strip special characters, like carriage or line returns and tabs.
function set_client_info($c,$userid)
{
  // Declare a PL/SQL execution command.
  $stmt = &quot;BEGIN
             dbms_application_info.set_client_info(:userid);
           END;&quot;;

  // Strip special characters to avoid ORA-06550 and PLS-00103 errors.
  $stmt = strip_special_characters($stmt);

  // Parse a query through the connection.
  $s = oci_parse($c,$stmt);

  // Map the local variable to a bind variable.
  oci_bind_by_name($s,&#39;:userid&#39;,$userid);

  // Run the procedure.
  if (oci_execute($s))
    return true;
  else
    return false;
}
</pre>
<p><strong>The strip_special_characters() Function. </strong>The strip_special_characters() function removes tabs and line returns that cause problems with the PL/SQL parser. This enables you use whitespace and line returns to make your code more readable. The function is:</p>
<pre>function strip_special_characters($str)
{
  $out = &quot;&quot;;
  for ($i = 0;$i &lt; strlen($str);$i++)
    if ((ord($str[$i]) != 9) &amp;&amp; (ord($str[$i]) != 10) &amp;&amp;
        (ord($str[$i]) != 13))
      $out .= $str[$i];
  // Return pre-parsed SQL statement.
  return $out;
}
</pre>
<h2>Conclusion</h2>
<p>You have now learned how identity management works in the context of virtual private databases for scheme, and how to configure the application for individual users found in an ACL within a single schema. The code examples and analyses show how you can operate selectively based striping values of authenticated users. The users can be stored in an ACL table that resides in a single schema. You can now extend the provided solution into a VPD model by simply building the policy functions against the session CLIENT_INFO column value.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.allfreetech.com/php/database-based-authentication-for-php-apps-part-2-52.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Database-Based Authentication for PHP Apps, Part 1</title>
		<link>http://www.allfreetech.com/php/database-based-authentication-for-php-apps-part-1-50.html</link>
		<comments>http://www.allfreetech.com/php/database-based-authentication-for-php-apps-part-1-50.html#comments</comments>
		<pubDate>Sat, 23 Jan 2010 03:17:00 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Database]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://www.allfreetech.com/?p=50</guid>
		<description><![CDATA[Managing secure access to Web pages and applications is a common problem. You want to enable those you trust to access data while preventing unauthorized ones from gaining access to it. In most cases, database-based authentication is the solution. Authentication systems contain an Access Control List (ACL) that lists your user credentials and matches them [...]]]></description>
			<content:encoded><![CDATA[<p>Managing secure access to Web pages and applications is a common problem. You want to enable those you trust to access data while preventing unauthorized ones from gaining access to it. In most cases, database-based authentication is the solution.</p>
<p>Authentication systems contain an Access Control List (ACL) that lists your user credentials and matches them to your assigned system privileges. Credentials are typically a user name and password pair. Credentials link your users to system privileges. System privileges let your accounts access or modify data, and enable your accounts to execute subsystems or subroutines. Your accounts can be users, groups, or systems.<span id="more-50"></span></p>
<p>In this article, you will learn how to implement this concept in PHP-based Web applications. You will learn how to design and implement an authentication database model, and plan and manage all aspects of user interactions in a browser-hosted application.</p>
<p>In <span class="bodylink">Part 2</span> of this article, you will explore different methods of database access and how authentication systems can leverage security policies in an <a href="http://www.oracle.com/technology/deploy/security/database-security/virtual-private-database/index.html">Oracle Virtual Private Database</a> (VPD), as well as with the built-in DBMS_APPLICATION_INFO package.</p>
<h2>Architecture, Authentication, and Encryption</h2>
<p><strong>Architecture. </strong>When Web applications request information from the Apache HTTP server, they send information from the client to the server. Users view this process as submitting a URL and receiving a Web page in reply. A URL is only the exposed part of the URI; the URI contains HTTP headers, cookies, and a URL. The information is transmitted as an encoded name and value pair in the URI.</p>
<p>Cookies are small text files containing clear or encrypted text. Cookies contain the current transaction state of communication between the browser and server application. The content of cookies was once frequently attached to the URI to help maintain transaction state. A single session cookie now sends a numeric reference that enables the prior name and value cookie pairs to be stored securely on the server. The numeric reference is known as a Web session ID.</p>
<p>When using Web application sessions, the session ID and expiration information about the session are sent as a special session cookie. When client browsers disallow cookies, Web applications may default to URL rewriting, which redirects the session cookie from the undisclosed URI to the disclosed URL. You recognize two benefits by leaving the data on the server and storing it by a unique session ID &mdash; you minimize the size of the URL, and you secure the internal details of user interactions.</p>
<table align="right" bgcolor="#eeeeee" border="1" cellpadding="4" cellspacing="0" class="bodycopy" width="30%">
<tbody>
<tr>
<td colspan="3">
<h2>Sample Code Manifest</h2>
</td>
</tr>
<tr>
<td valign="middle" width="175"><strong>Program Name</strong></td>
<td valign="middle" width="96"><strong>Language</strong></td>
<td valign="middle" width="371"><strong>Description</strong></td>
</tr>
<tr>
<td>AddDbUser.php</td>
<td>PHP</td>
<td>Program file that authenticates new users and returning active sessions; and renders a form to enter new users into an ACL.</td>
</tr>
<tr>
<td>create_identity_db1.sql</td>
<td>SQL</td>
<td>Script that builds the database and seeds necessary rows to initialize the Web application</td>
</tr>
<tr>
<td>Credentials1.inc</td>
<td>PHP</td>
<td>Include file that defines the three global constants you use in all oci_connect() function calls</td>
</tr>
<tr>
<td>SignOnDB.php</td>
<td>PHP</td>
<td>Program file that esets the PHP session ID before calling the AddDbUser.php program</td>
</tr>
<tr>
<td>SignOnRealm.php</td>
<td>PHP</td>
<td>Program file that authenticates credentials and then renders the authentication values to a static Web page</td>
</tr>
</tbody>
</table>
<p>Although keeping session information on the server is more secure, you must guard against session hijacking. A malicious user can hijack a session by (a) stealing a session cookie, (b) copying a URL that includes the session ID or (c) snatching it through a man-in-the-middle attack. Modern browsers make this more difficult, and PHP does not write the physical session cookies to a file system.</p>
<p>A benefit of the cookie and session architecture is that you can mine historical session data, provided you store data in a normalized data model. The data let you discover how customers use your Web applications. The data also let you discover customer browsing patterns and purchasing trends. This type of information enables businesses to target marketing to individuals as opposed to groups.</p>
<p><strong>Authentication. </strong>You will use one of two authentication models to verify users in your Web applications. Your choice is between the basic HTTP/HTTPS authentication, and the cookie and session authentication models. A third authentication model, a digest HTTP authentication, is under development but appears likely to remain an incomplete solution for a few more years.</p>
<p>Both of the first two authentication models let you take credentials from a Web page and validate them against an ACL. They also let you work successfully whether or not the browser accepts cookies. The basic HTTP/HTTPS and digest HTTP methods validate against a realm without using a cookie, while the session management writes at least a session ID cookie.</p>
<p>URL rewriting presents some security risks when you send the session ID as part of the URL because some users instant message the URL to someone else. That other person may hijack the authorization to access or compromise confidential data. An alternative to URL rewriting is placing the session ID into the rendered Web page as a hidden field. While this does create vulnerability, it is less risky than appending the session to the URL.</p>
<p>Your PHP authentication script takes a name and value pair for the user name and password. The script then compares the pair against data stored in your ACL. This is the same process in either the discussed authentication models. While it is customary to store server-side passwords in encrypted forms, you should recognize that any compromise of the clear text user and password values compromises your system security.</p>
<p>Therefore, you should not force users to create complex passwords because they aren&#39;t easily remembered. Users write hard-to-remember passwords on notepads or yellow sticky pads. Written passwords are security risks to your Web application because hackers might find and misuse them.</p>
<p><strong>Encryption. </strong>Encryption is always a hot topic in Web application development, deployment, and management.</p>
<p>There are two encryption techniques in PHP Web applications. The first involves encrypting your protocol. Encrypted protocols, like HTTPS, protect you from man-in-the-middle attacks using network analyzers, commonly known as packet sniffers. The second involves encrypting your user password from exposure to prying eyes on the server.</p>
<p>Password encryption protects your system from attacks best when the encrypted passwords are stored separately from the source code. When the ACL is a file on the file system, both the method of encryption and encrypted values can be compromised by a single server hack. You reduce the risk of compromising the encrypted passwords by storing your ACL in a database.</p>
<h2>Identity Management Data Modeling</h2>
<p>Building an identity management data model can vary from the simple to the complex. The simplest model is a single table containing the ACL for your application. Unfortunately, this model only works with basic HTTP/HTTPS authentication.</p>
<p>The simplest way to implement cookie and session authentication requires at least two tables. One table contains the ACL, and the other contains the session data. The following depicts a basic model:</p>
<p align="center"><img src="http://www.oracle.com/technology/pub/images/mclaughlin-phpid1-f1.gif" /></p>
<p>	<strong>Figure 1</strong> Basic authentication data model</p>
<p>A more effective solution for authentication would support capturing and mining user interactions. These vary from the list of Web pages called to individual events, like mouse clicks. Figure 2 shows a data model that can capture user navigation and both successful and unsuccessful logins. For our example, this data model is created by the create_identity_db.sql script (see sample code zip).</p>
<p align="center"><img src="http://www.oracle.com/technology/pub/images/mclaughlin-phpid1-f2.gif" /></p>
<p>	<strong>Figure 2</strong> Detailed authentication data model</p>
<p>The larger model lets you track versions of defined modules against runtime calls to modules. Actual parameters map to catalog items, like books on Amazon.com. An access log is added to track multiple connections made during a single session. The full details of how you implement your model can vary based on your goals. Figure 2 also shows an example model that supports version control in a catalog ordering application. At present, the sample code only captures invalid log in attempts and the ACCESS_LOG table is renamed to reflect that difference as INVALID_SESSION table.</p>
<h2>Authentication Process Models</h2>
<p>Basic HTTP/HTTPS authentication operates by establishing a browser&#39;s credentials in a realm. Browsers can concurrently support many realms. Realms are set in the header of XHTML documents. They can be manually coded in the form, or set in the AuthName directive of the httpd.conf file. Realms act as guards over protected server areas, and they can apply to one or more protected server areas. One advantage of basic HTTP/HTTPS authentication is that you don&#39;t have to code a login or logout facility. Browsers provide the login forms and you logout by closing all open browser windows.</p>
<p>A major downfall of the basic HTTP/HTTPS authentication method is you must close all browser windows to end active realm authorizations. This means that a user can authenticate, walk away from the terminal while leaving an open browser, and <em>enable someone to compromise their confidential data by hijacking their identity</em>.</p>
<p>Cookie and session authentication operates by checking user credentials against your ACL. When the ACL is stored in a file, this means reading the file and comparing the name value pairs of a user name and password against a submitted clear text user name and encrypted password.</p>
<p>When the ACL is stored in a database, this means one thing on initial login and another on subsequent connections. On initial login, you connect to the database, read the user name and encrypted password values from a database table; and compare the results against the submitted clear text user name and encrypted password. On subsequent Web requests, you connect to the database, read the session ID, and compare results against the submitted session ID. Unlike basic HTTP/HTTPS authentication, you can successfully logout while leaving the Web browser open.</p>
<p>While the session ID becomes a potential security risk because of session hijacking, some reasonable precautions can minimize this risk. The biggest security gap can be closed by implementing your login page as a PHP script instead of a simple text XHTML form. The sample cookie and session code demonstrates this approach. It ensures that a second user on a shared machine doesn&#39;t gain entry from the prior credentials. The implementation details are found in the following Cookie and Session Authentication Model section.</p>
<p><strong>Basic HTTP/HTTPS Authentication Model. </strong>The basic HTTP/HTTPS authentication model authorizes once for a realm and revokes the realm authentication only when all browser windows are closed. The activity diagram representing the model from within the browser context is shown in Figure 3. Microsoft Internet Explorer provides three attempts before failing authentication but you can simply refresh the page to get another three successive attempts. The Firefox browser differs by prompting until the user cancels the authentication process.</p>
<p align="center"><img src="http://www.oracle.com/technology/pub/images/mclaughlin-phpid1-f3.gif" /></p>
<p>	<strong>Figure 3</strong> Basic HTTP/HTTPS authentication activity diagram</p>
<p>You start a Web application session by entering a URL for the login form, which then posts to the server and sends a message back to the browser requesting that it collect and send for validation the user&#39;s credentials. When you submit the credentials to the SignOnDB.php program, the code attempts to validate your log credentials. When it works you can then use any Web pages in the realm. Authentication failure re-prompts with the browser login form as noted previously.</p>
<p>When implementing basic HTTP/HTTPS authentication, PHP uses three predefined variable names in the $_SERVER array. Basic HTTP/HTTPS authentication puts the user ID and the password in the $_SERVER[&#39;PHP_AUTH_USER&#39;] and $_SERVER[&#39;PHP_AUTH_PW&#39;] respectively. When the third, HTTP_AUTHORIZATION, is not set, you submit these two $_SERVER values to your authentication function. After your server-side program authenticates the user, subsequent reads of the $_SERVER array HTTP_AUTHORIZATION name let the browser access to the realm until it is closed.</p>
<p>An easy way to protect your pages in the basic HTTP/HTTPS authentication model is to include a Boolean control variable that is false until authentication is verified. You display content only when the Boolean control variable becomes true. Your code should check for authentication, and prompt for credentials when you are not authenticated in the realm. <em>This code should be found in all pages on your secure Web site.</em></p>
<p>You trigger HTTP header validation in your server-side script by checking the values of these predefined $_SERVER variables. The request to the SignOnRealm.php page places these values in the returned header, which instructs the browser to prompt with a credential entry dialog box. Clicking the credentials OK button sends a new request to the server-side script. The server-side script then attempts to validate the submitted credentials unless a user cancels the process. These realm sign in scripts typically return a failure message when the user cancels the process. Some sites also shut user accounts when there are three or four consecutive authentication failures because someone may be attempting to hack into the system through a known user name.</p>
<p><strong>Setup Test Application. </strong>The demonstration application uses an Oracle schema named IDMGMT1, and the password is the same as the schema. You can create the user and environment by following these steps:</p>
<p>1. Log into the database as the privileged SYSTEM user and run the following commands:</p>
<pre>SQL&gt; CREATE USER IDMGMT1 IDENTIFIED BY IDMGMT1;
SQL&gt; GRANT CONNECT, RESOURCE TO IDMGMT1;
</pre>
<p>2. Connect to the new user schema:</p>
<pre>SQL&gt; CONNECT IDMGMT1/IDMGMT1@XE
</pre>
<p>3. Run the create_identity_db1.sql script in the IDMGMT1 schema to create all necessary objects and seed the SYSTEM_USER table:</p>
<pre>SQL&gt; @create_identity_db1.sql
</pre>
<p>4. Place the following files in your htdocs directory or a subdirectory of the htdocs directory:</p>
<pre> &bull; SignOnRealm.php
 &bull; SignOnDB.php
 &bull; AddDbUser.php
</pre>
<p>These steps complete the setup required for our purposes here. You can now experiment with either the realm or session identification management examples.</p>
<p><strong>Experiment with Basic HTTP/HTTPS Authentication. </strong>You can experiment with the realm identity management by following these steps:</p>
<p>1. Open the realm identification management examples by using the following URL:</p>
<pre>http://localhost/SignOnRealm.php
</pre>
<p>2. You can use either of the following seeded accounts as valid credentials using Basic HTTP/HTTPS authentication. Make sure you don&#39;t have the cap lock enabled because they are case sensitive.</p>
<table align="center" border="1" cellpadding="4" cellspacing="0" class="bodycopy">
<tbody>
<tr>
<td bgcolor="#eeeeee" width="156"><strong>User Name</strong></td>
<td bgcolor="#eeeeee" width="148"><strong>Password</strong></td>
</tr>
<tr>
<td>administrator</td>
<td>welcome</td>
</tr>
<tr>
<td valign="top">guest</td>
<td valign="top">guest</td>
</tr>
</tbody>
</table>
<p align="center"><img src="http://www.oracle.com/technology/pub/images/mclaughlin-phpid1-f4.gif" /></p>
<p>	<strong>Figure 4</strong> Basic HTTP/HTTPS authentication dialog box</p>
<p>After you successfully enter your credentials you will be presented with the following page that echoes back the plain text credentials and encrypted password:</p>
<p align="center"><img src="http://www.oracle.com/technology/pub/images/mclaughlin-phpid1-f5.gif" /></p>
<p>	<strong>Figure 5</strong> Basic HTTP/HTTPS credential validation acknowledgment</p>
<p><strong>Analyze the Basic HTTP/HTTPS Authentication Code. </strong>After you enter your user name and password, your script will call the verify_db_login() function. The following PHP code demonstrates how you begin the authentication of users in your realm:</p>
<pre>// Declare control variable.
$valid_user = false;

// Authenticate user.
if ((isset($_SERVER[&#39;PHP_AUTH_USER&#39;])) &amp;&amp; (isset($_SERVER[&#39;PHP_AUTH_PW&#39;])))
  if (verify_db_login($_SERVER[&#39;PHP_AUTH_USER&#39;],$_SERVER[&#39;PHP_AUTH_PW&#39;]))
    $valid_user = true;
</pre>
<p>This function opens a connection to the Oracle database and checks whether the credentials are valid. The verify_db_login() function returns true when credentials are valid and false when not. When the SYSTEM_USER table is missing, the code tells you to check for the missing table. The main code for the Web page is shown in the function:</p>
<pre>// Check for authorized account.
function verify_db_login($userid,$passwd)
{
  // Attempt connection.
  if ($c = @oci_connect(SCHEMA,PASSWD,TNS_ID))
  {
    // Return a row.
    $s = oci_parse($c,&quot;SELECT   NULL
                       FROM     system_user
                       WHERE    system_user_name = :userid
                       AND      system_user_password = :passwd
                       AND      SYSDATE BETWEEN start_date
                                        AND NVL(end_date,SYSDATE)&quot;);

    // Encrypt password.
    $newpassword = sha1($passwd);
    // Bind variables as strings.
    oci_bind_by_name($s,&quot;:userid&quot;,$userid);
    oci_bind_by_name($s,&quot;:passwd&quot;, $newpassword));

      // Execute the query.
      if (@oci_execute($s,OCI_DEFAULT))
      {
        // Check for a validated user, also known as a fetched row.
        if (oci_fetch($s))
           return true;
        else
          return false;
      }
      else
      {
        // Print error when execution fails.
        $errorMessage = &quot;Check for a missing SYSTEM_USER table.&lt;br /&gt;&quot;;
        print $errorMessage;
      }

    // Close connection.
    oci_close($c);
  }
  else
  {
    $errorMessage = oci_error();
    print htmlentities($errorMessage[&#39;message&#39;]).&quot;&lt;br /&gt;&quot;;
  }
}
</pre>
<p>There are two things to note about the verify_db_function() function. First, the query returns a null value, which eliminates processing any unnecessary return values. Second, the clear text password is encrypted using the sha1() function and assigned to a local variable.</p>
<p>You now know how to build the required components for basic HTTP/HTTPS authentication. The two caveats associated with this authentication model are that you: (1) have complete access to the realm once you authenticate your credentials in a browser; and (2) can only log out by closing <strong>all</strong> browser windows.</p>
<p><strong>Cookie and Session Model. </strong>The cookie and session model is popular because you can let users login and logout of Web applications without requiring them to shutdown all of their browser windows. This authentication model is also more complex than the basic HTTP/HTTPS model. There are different ways that you can implement cookie and session authentications.</p>
<p>The login and logout operations operate inside active browsers. This operating context adds more complexity to the cookie and session model. For example, the browser backward, forward, and refresh buttons require special handling in your code. A refresh button shouldn&#39;t attempt to send a new user authentication more than once. Likewise, a refresh button should not trigger new authentication requests after a user has logged out of an application. Unfortunately, they do unless the program prevents it.</p>
<p>Calling a login PHP script, not an XHTML Web page is the most direct way to guarantee a complete logout operation in your Web application. A PHP script can reset the session value whereas an XHTML Web page cannot because it passes the prior session value when submitting missing or new credentials to a target page.</p>
<p>There are several predefined variables in PHP that you can use to store globally scoped variables. One is the $_SESSION array. It lets you add any name-value pair, where the name becomes the index value to the actual data value. The advantages of using the $_SESSION array are simplicity and flexibility but the disadvantages can involve conflicts with other libraries.</p>
<p>The session_start() function returns the active PHP session ID value. You must call the session_start() function before the session_regenerate_id() function because it actually calls the session_destroy() function. The session_destroy() depends on the existence of a PHP session ID and will raise an error when one doesn&#39;t exist before you call it. Your call to the session_regenerate_id() function with an actual true parameter will reset the PHP session ID. You use runtime error suppression to guarantee that calling the session_regenerate_id() function doesn&#39;t raise an error when no prior session exists in the context of the browser.</p>
<pre>&lt;?php
  // Start and regenerate session.
  session_start();
  @session_regenerate_id(true);
  $_SESSION[&#39;sessionid&#39;] = session_id();
?&gt;
</pre>
<p>The preceding scriptlet alters the session value in the browser, and it will force new authentication with the next click of the <strong>Login</strong> button. It does this by replacing the previously authenticated PHP session ID with a new session ID. The new PHP session ID is transmitted as part of the URL to the next form.</p>
<p>There are two ways to manage database connections once you establish your PHP session: One uses a persistent connection between the PHP module and the database and the other uses a non-persistent connection between the two. Persistent connections let you start a transaction scope that can span several Web requests, and they end only when you issue a database commit command. During the scope of a persistent connection, you can run SQL and PL/SQL statements that lock rows pending change or after an uncommitted change. Non-persistent connections let you run SQL or PL/SQL statements to query or change data between opening and closing a connection, which occurs in a single Web request. Data transactions limited by a single connection are also known as autonomous transactions because their duration is limited to the duration of the connection.</p>
<p>These sample programs use non-persistent database connections to query and transact against the Oracle database. Consequently, all database transactions perform as autonomous transactions.</p>
<p>The SignOnDB.php Web page, shown in Figure 5, implements this type of PHP scriptlet before the XHTML page. The XHTML rendering found in the SignOnDB.php form is also found in the AddDbUser.php page as the signOnForm() function.</p>
<p>You will put the PHP authentication logic in all Web pages called after a user logs into the application. At present all programming logic is in the AddDbUser.php page, shown in Figure 6, but you can move the twelve functions into one authentication library.</p>
<p>Your browser must accept cookies for these programs to work. When cookies are disabled, all you can do is login, logout, and fail to enter a new user because the session values are lost between pages.</p>
<p><strong>Experiment with Session Authentication. </strong>You can experiment with the session identity management by following these steps:</p>
<p>1. Open the session identification management examples by using the following URL, assuming that you placed the code in the htdocs directory:</p>
<pre>http://localhost/SignOnDB.php
</pre>
<p>2. You can use either of the seeded accounts presented in the Basic HTTP/HTTPS authentication:</p>
<p align="center"><img src="http://www.oracle.com/technology/pub/images/mclaughlin-phpid1-f6.gif" /></p>
<p>	<strong>Figure 6 </strong>Cookie and Session Login page</p>
<p>3. You can now enter a new user into your credential repository in the <strong>New User</strong> form, which requires user name to be a string that is 6 to 10 characters long and starts with a letter:</p>
<p align="center"><img src="http://www.oracle.com/technology/pub/images/mclaughlin-phpid1-f7.gif" /></p>
<p>	<strong>Figure 7</strong> Cookie and session Add New User page</p>
<p>4. After you add the new user, the AddDBUser.php script will present you with a new copy of the <strong>New User</strong> form. It will acknowledge whether you successfully added a new user or explain what rule the entry violated. You can connect to the IDMGMT1 schema and run the following query to inspect a new user entry:</p>
<pre>SELECT system_user_name, system_user_password FROM system_user;
</pre>
<h4>Analyze the Session Authentication Code</h4>
<p>The PHP program&#39;s logic accounts for the typical nuances of the forward, backward and refresh browser buttons. Figure 7 displays the rendered Web page. When you call or refresh the form by using the browser navigation buttons, the program determines whether (a) the session is registered in the database model to the current user, (b) the remote address is the same client IP address from authentication, and (c) the session is current. A session is current when the last related database activity of the session occurred within the last five minutes.</p>
<p>Both the initial sign on form and add user forms check and verify a registered session before the program logic looks for new user credentials and attempts to authenticate the user. When the credentials are authenticated, the SignOnDB.php script registers the new session and calls the AddDbUser.php script. A refresh of the AddDbUser.php script calls the signOnForm() function that mimics the SignOn.php form&#39;s behavior. When the AddDbUser.php script authenticates credentials, it renders a page using the addUserForm() and when either script repudiates credentials, they log a failed login attempt and render a new sign on page.</p>
<p align="center"><img src="http://www.oracle.com/technology/pub/images/mclaughlin-phpid1-f8.gif" /></p>
<p>	<strong>Figure 8</strong> Cookie and session authentication activity diagram</p>
<p>The Web page contains two XHTML forms. One form lets you enter a new user, validate that the credentials meet guidelines (e.g., 6 to 10 character strings starting with a letter), and inserts the credentials into the database. The other form lets you opt to log out of the application by calling the SignOnDB.php script, which resets the session to prevent it appearing authenticated in the database after a user logged out.</p>
<p>The Web page starts the session and assigns it to the $_SESSION array, assigns any submitted credentials to local variables, and then checks to see whether a current authenticated session is available. This is done by the following code:</p>
<pre>  // Check for valid session and regenerate when session is invalid.
  if ((get_session($_SESSION[&#39;sessionid&#39;],$userid,$passwd) == 0) ||
      (($_SESSION[&#39;userid&#39;] != $userid) &amp;&amp; ($userid)))
  {
    // Regenerate session ID.
    session_regenerate_id(true);
    $_SESSION[&#39;sessionid&#39;] = session_id();
  }
  else
  {
    $authenticated = true;
  }
</pre>
<p>The get_session() function opens a connection to the database and queries a result set from the join of the SYSTEM_USER and SYSTEM_SESSION tables. The results from the query determine whether or not the session is authenticated. Part of this verification process checks to make sure that the request originates from the same IP address. When authenticated, Web page variables are assigned values from the query to process the page request. At the same time, the LAST_UPDATE_DATE column timestamp is updated by the update_session() function. The attempt is logged by the record_session() function to the OBSOLETE_SESSION table in the database when authentication fails.</p>
<pre>// Get a valid session.
  function get_session($sessionid,$userid = null,$passwd = null)
  {
    // Attempt connection and evaluate password.
    if ($c = @oci_connect(SCHEMA,PASSWD,TNS_ID))
    {
      // Assign metadata to local variable.
      $remote_address = $_SERVER[&#39;REMOTE_ADDR&#39;];

      // Return database UID within 5 minutes of session registration.
      // The Oracle DATE data type is a timestamp where .003472222 is
      // equal to 5 minutes.
      $s = oci_parse($c,&quot;SELECT   su.system_user_name
                         ,        ss.system_remote_address
                         ,        ss.system_session_id
                         FROM     system_user su JOIN system_session ss
                         ON       su.system_user_id = ss.system_user_id
                         WHERE    ss.system_session_number = :sessionid
                         AND     (SYSDATE - ss.last_update_date) &lt;=
                                    .003472222&quot;);

      // Bind the variables as strings.
      oci_bind_by_name($s,&quot;:sessionid&quot;,$sessionid);

      // Execute the query, error handling should be added.
      if (@oci_execute($s,OCI_DEFAULT))
      {
        // Check for a validated user, also known as a fetched row.
        if (oci_fetch($s))
        {
           // Assign unqualified values.
          $_SESSION[&#39;userid&#39;] = oci_result($s,&#39;SYSTEM_USER_NAME&#39;);

          // Check for same remote address.
          if ($remote_address == oci_result($s,&#39;SYSTEM_REMOTE_ADDRESS&#39;))
          {
            // Refresh last update timestamp of session.
            update_session($c,$sessionid,$remote_address);
            return (int) oci_result($s,&#39;SYSTEM_SESSION_ID&#39;);           }
           else
           {
             // Log attempted entry.
             record_session($c,$sessionid);
             return 0;
           }
        }
        else
        {
          // Record when not first login.
          if (!isset($userid) &amp;&amp; !isset($passwd))
            record_session($c,$sessionid);

          // Return a zero.
          return 0;
        }
      else
      {
        // Print error when oci_execute() fails.
        $errorMessage = &quot;Check for a missing SYSTEM_USER or &quot;;
        $errorMessage .= &quot;SYSTEM_SESSION tables.&lt;br /&gt;&quot;;
        print $errorMessage;
        return 0;
      }

      // Close the connection.
      oci_close($c);
    }
    else
    {
      $errorMessage = oci_error();
      print htmlentities($errorMessage[&#39;message&#39;]).&quot;&lt;br /&gt;&quot;;
      return 0;
    }
  }
</pre>
<p>You should note that the functions called from the get_session() function share the database connection opened to confirm an existing session. This goal is accomplished by passing the connection as an actual parameter to the other two functions. Managing the database transaction scope in a single database connection lets you manage multiple SQL and PL/SQL statements as components of a transaction. This enables functions to contain single subjects of your data abstraction layer and lets you treat nested function calls as subroutines.</p>
<p align="center"><img src="http://www.oracle.com/technology/pub/images/mclaughlin-phpid1-f9.gif" /></p>
<p>	<strong>Figure 9</strong> Cookie and session Add New User Bad Credential page</p>
<p>The next section of primary code in the Web page renders different versions of the form because it serves multiple purposes. Figure 6 shows the form rendered on initial login to the application. Figure 9 demonstrates the form rendered after attempting to enter a null user name. The code calls the create_new_db_user() function when appropriate to add new users to the ACL in the SYSTEM_USER table, as shown:</p>
<pre>  // Check whether the program should:
  // -----------------------------------------------------------------
  //  Action #1: Verify new credentials and start a database session.
  //  Action #2: Continue a session on refresh button.
  //  Action #3: Provide a new form after adding a user.
  //  Action #4: Provide a new form after failing to add a user.
  // -----------------------------------------------------------------
  if (($authenticated) || (authenticate($userid,$passwd)))
  {
    // Assign inputs to variables.
    $newuserid = @$_POST[&#39;newuserid&#39;];
    $newpasswd = @$_POST[&#39;newpasswd&#39;];

    // Set message and write new credentials.
    if ((isset($newuserid)) &amp;&amp; (isset($newpasswd)) &amp;&amp;
        (($code = verify_credentials($newuserid,$newpasswd)) !== 0))
    {
      // Render empty form with error message from prior attempt.
      addUserForm(array(&quot;code&quot;=&gt;$code
                       ,&quot;form&quot;=&gt;&quot;AddDbUser.php&quot;
                       ,&quot;userid&quot;=&gt;$newuserid));
    }
    else
    {
      // Create new user only when authenticated.
      if (!(isset($userid)) &amp;&amp; (isset($_SESSION[&#39;userid&#39;])))
       create_new_db_user($_SESSION[&#39;db_userid&#39;],$newuserid,$newpasswd);

      // Render fresh empty form.
      addUserForm(array(&quot;form&quot;=&gt;&quot;AddDbUser.php&quot;));
    }
  }
  else
  {
    // Destroy the session and force re-authentication.
    session_destroy();

    // Redirect to the login form.
    signOnForm();
  }
</pre>
<p>The nested if statement filters calls to the create_new_db_user() function by ensuring that $newuserid and $newpasswd variables are set. These two variables can only be set in the AddDbUser.php Web page, which means these cannot be called until after your user has authenticated. This works on a subsequent call to the AddDbUser.php Web page because clicking the <strong>Add User</strong> button recursively calls the same Web page.</p>
<pre>  // Add a new user to the authorized control list.
  function create_new_db_user($userid,$newuserid,$newpasswd)
  {
    // Attempt connection and evaluate password.
    if ($c = @oci_connect(SCHEMA,PASSWD,TNS_ID))
    {
      // Check for prior insert, possible on Web page refresh.
      if (!is_inserted($c,$newuserid))
      {
        // Encrypt password.
        $newpassword = sha1($passwd);

        // Return database UID.
        $s = oci_parse($c,&quot;INSERT INTO system_user
                           ( system_user_id
                           , system_user_name
                           , system_user_password
                           , system_user_group_id
                           , system_user_type
                           , created_by
                           , creation_date
                           , last_updated_by
                           , last_update_date )
                           VALUES
                           ( system_user_s1.nextval
                           , :newuserid
                           , :newpasswd
                           , 1
                           , 1
                           , :userid1
                           , SYSDATE
                           , :userid2
                           , SYSDATE)&quot;);

        // Bind the variables as strings.
        oci_bind_by_name($s,&quot;:newuserid&quot;,$newuserid);
        oci_bind_by_name($s,&quot;:newpasswd&quot;, $newpassword);
        oci_bind_by_name($s,&quot;:userid1&quot;,$userid);
        oci_bind_by_name($s,&quot;:userid2&quot;,$userid);

        // Execute the query, error handling should be added.
        if (!@oci_execute($s,OCI_COMMIT_ON_SUCCESS))
        {
          // Print error when oci_execute() fails.
          $errorMessage = &quot;Check for a missing SYSTEM_USER table.&lt;br /&gt;&quot;;
          print $errorMessage;
        }
      }

      // Close the connection.
      oci_close($c);

    }
    else
    {
      $errorMessage = oci_error();
      print htmlentities($errorMessage[&#39;message&#39;]).&quot;&lt;br /&gt;&quot;;
    }
  }
</pre>
<p>Inside of the create_new_db_user() function, the function makes a call to the is_inserted() function. This checks to make sure that the user does not already exist before attempting to insert one into the SYSTEM_USER table. Like the earlier nested function examples, the is_inserted() function shares the local connection for transaction control purposes. Also, the sha1() function converts the clear text password to an encrypted string before binding the user password to the data manipulation variable.</p>
<p>After successfully inserting the new user, the <strong>New User</strong> form renders itself again waiting for you to insert another user or to click the <strong>Log Out</strong> button. The <strong>Log Out </strong>button returns you to the login screen, which resets your session identifier.</p>
<h2>Conclusion</h2>
<p>Now that you have learned how identity management works and how you can implement a basic identity management solution, you should be comfortable with the terminology, architecture, and approach to authenticating your users.</p>
<p>You can now manage user authentication and access equally, but all users are not equal. Some may have unrestricted access, while most others have restricted access privileges. Part 2 of this article shows you how to link identity management with two technologies that enable fine-grained access control.</p>
<p>Although the VPD feature is the &quot;state of the art,&quot; the older technology, DBMS_APPLICATION_INFO, also works in Oracle8<em>i</em>, Oracle9<em>i, </em>and Oracle Database 10<em>g</em> Release 1. It is also the core utility supporting Oracle E-Business 11i Suite authentication. Both these technologies let you implement fine grained access privileges and roles.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.allfreetech.com/php/database-based-authentication-for-php-apps-part-1-50.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Migrating Web-Based PHP Applications to Ajax</title>
		<link>http://www.allfreetech.com/php/migrating-web-based-php-applications-to-ajax-46.html</link>
		<comments>http://www.allfreetech.com/php/migrating-web-based-php-applications-to-ajax-46.html#comments</comments>
		<pubDate>Sat, 23 Jan 2010 03:14:04 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Ajax]]></category>

		<guid isPermaLink="false">http://www.allfreetech.com/?p=46</guid>
		<description><![CDATA[Web development is messy. Over the years, our toolbox has filled up with odd implements that are hard to use and don&#39;t fit well together. Web code has become a legacy problem. A typical web page is a tangle of HTML, JavaScript, and server-side scripts. User interface logic is interwoven with business rules and client-server [...]]]></description>
			<content:encoded><![CDATA[<p>Web development is messy. Over the years, our toolbox has filled up with odd implements that are hard to use and don&#39;t fit well together. Web code has become a <em>legacy</em> problem. A typical web page is a tangle of HTML, JavaScript, and server-side scripts. User interface logic is interwoven with business rules and client-server communications. In most programming environments, we use documented APIs, so you just pass arguments to a function and get back the results. In the web environment, we&#39;ve typically needed hacks like populating hidden fields in a form, and regenerating the whole page, even for a tiny change. Can we make the process more rational?<span id="more-46"></span></p>
<p>This article describes a makeover of a typical database-backed web form. We&#39;ll show some old code &ndash; a mixture of HTML, JavaScript, and PHP &ndash; and rebuild it with modern web techniques like Ajax, and modern tools like jQuery. The benefits will include:</p>
<ul>
<li>Separating dynamic content from static content.</li>
<li>Separating content, style, and processing.</li>
<li>Web client-server communication via function calls.</li>
<li>Partial page updates instead of flash-bang page reloads.</li>
<li>Faster development and more maintainable code.</li>
<li>Faster load times and improved caching.</li>
</ul>
<h3>The Old Grey Web</h3>
<p>In the beginning was HTML, shortly followed by forms, client-side JavaScript, and server-side CGI scripts. You would fill in form variables and submit the form to the CGI script, or generate a long GET-style URL in JavaScript. JavaScript was close to undebuggable. Core variables like <code>window</code> were not part of the language, and the Microsoft-Netscape browser wars introduced gratuitous differences that continue to plague us. Things got a little better over time, as the W3C defined the <a href="http://www.w3.org/DOM/">DOM</a> and developers built cross-platform <em>DHTML</em> libraries.</p>
<p>Some dynamic pages are easy, you query the database once and dump some nicely formatted HTML, but most are harder. Database values populate pull-down menus (HTML <code>select</code> and <code>option</code> tags) and choices (<code>radio</code> and <code>checkbox</code> tags). Some forms cover multiple pages, and the logic for maintaining overall state gets trickier. Every form submission returns a fully regenerated page, maintaining all the state of the previous submissions.</p>
<p>Traditional options on how to structure the application include:</p>
<ul>
<li>Put everything in a single script. On the first call, query the database and generate HTML and form elements. On subsequent calls, the form passes a <em>command</em> (such as <code>add</code>, <code>change</code>, or <code>delete</code>) and any <em>arguments</em> (such as the person&#39;s ID, if <code>change</code> or <code>delete</code>). The script modifies the database and again rewrites the page&#39;s content. The form elements must reflect the database changes.</li>
<li>Use two scripts. The first queries the database and generates the HTML, with two differences from the previous example:
<ul>
<li>The form has an iframe for displaying dynamic contents.</li>
<li>The form&#39;s <code>action</code> is the second script.</li>
<li>The form&#39;s <code>target</code> is the name of the iframe.</li>
</ul>
<p>		Now the form is generated once and its elements maintain their values across form submissions. The output of the second script is displayed in the iframe.</li>
</ul>
<p>The second option sounds better, but it still has problems: if an action changed the data underlying the form elements in the enclosing page, the whole page needs to be regenerated. We need a third option.</p>
<h3>The New Toolbox</h3>
<p>The common restriction is the whole-page design. No matter what you do, no matter how small the change, you submit it to the server and get back a brand new page (an iframe is like a mini-page, and its size can&#39;t be changed). A variety of <a href="http://onlamp.com/pub/a/php/2007/05/10/migrating-web-based-php-applications-to-ajax.html?">JavaScript remoting</a> techniques have tried to make the web client-server connection work more like a procedure call. I think the most useful contributions are:</p>
<ul>
<li>innerHTML</li>
<li>XMLHttpRequest</li>
<li>Ajax</li>
<li>JSON</li>
<li>New JavaScript libraries</li>
</ul>
<dl>
<dt><code>innerHTML</code></dt>
<dd>This DOM attribute, introduced by Microsoft in IE4, is a de facto (not official W3C) standard supported by all modern browsers. With it, you get and set the contents between any HTML start and end tags, without a page refresh. This chunk of HTML:
<pre>&lt;p id=&quot;changeme&quot;&gt;Now you see it.&lt;/p&gt;</pre>
<p>can be modified by this chunk of JavaScript:
<pre>var obj = document.getElementById(&quot;changeme&quot;);
obj.innerHTML = &quot;Now you don&#39;t&quot;;</pre>
<p>to produce this:
<pre>&lt;p id=&quot;changeme&quot;&gt;Now you don&#39;t&lt;/p&gt;</pre>
<p>Although the DOM has functions to change page elements dynamically, <code>innerHTML</code> is simpler and <a href="http://www.quirksmode.org/dom/innerhtml.html">faster</a>. Unfortunately, <code>innerHTML</code> is a <a href="http://msdn.microsoft.com/workshop/author/dhtml/reference/properties/innerhtml.asp">read-only attribute</a> for many table elements in IE, forcing use of <a href="http://msdn.microsoft.com/workshop/author/tables/buildtables.asp">alternatives</a>.</dd>
<dt>&nbsp;</dt>
<dt><code>XMLHttpRequest</code></dt>
<dd>This is an API to send client requests to the server and receive the server responses. Microsoft invented it to make Outlook Web Access work more like a desktop application. It was included in IE5 and is supported by all modern browsers. Combined with <code>innerHTML</code>, you can call a server script and use the data returned to update any element on the page. Despite its utility, this function was hidden in plain sight for years (you wouldn&#39;t find it in JavaScript or DHTML books). Things changed when Google Mail, Maps, and Suggest demonstrated its responsiveness and introduced a new web application model.</dd>
<dt>&nbsp;</dt>
<dt>Ajax</dt>
<dd>The dam burst in 2005, when Jesse James Garrett named the new model <a href="http://adaptivepath.com/publications/essays/archives/000385.php">Ajax</a> (Asynchronous JavaScript and XML), comprising <code>XMLHttpRequest</code>, the DOM, and other techniques. The brand and its timing were perfect, rejuvenating web development and rehabilitating JavaScript.</dd>
<dt>&nbsp;</dt>
<dt>JSON</dt>
<dd>The old remoting frameworks used various formats for the client-server data stream, and <code>XMLHttpRequest</code>, as the name suggested, used XML. Doug Crockford designed the light and simple <a href="http://www.json.org/">JSON</a> (JavaScript Object Notation) format. It&#39;s trivial to parse JSON into JavaScript data structures (just <code>eval(<em>json_string</em>)</code>), and faster than deconstructing XML.</dd>
<dt>&nbsp;</dt>
<dt>JavaScript libraries</dt>
<dd>High-quality JavaScript libraries have been developed to simplify new-model web development and bridge the inevitable cross-browser issues. I&#39;ve chosen John Resig&#39;s <a href="http://www.jquery.com/">jQuery</a> over worthy competitors like <a href="http://www.prototypejs.org/">Prototype</a>, <a href="http://dojotoolkit.org/">Dojo</a>, or <a href="http://developer.yahoo.com/yui/">YUI</a> for a number of reasons:</p>
<ul>
<li>Compactness: About 19KB compressed</li>
<li>Chainability: Calls can be chained for more compact code</li>
<li>Support: Good <a href="http://docs.jquery.com/Main_Page">documentation</a> and <a href="http://docs.jquery.com/Discussion">development community</a></li>
<li>Flexibility: Its selector syntax includes CSS 1-3 and XPath phrases to select page elements</li>
<li>Architecture: Easy to extend through add-ons and plugins</li>
</ul>
</dd>
</dl>
<h3>The Makeover</h3>
<p>History class is over, and beauty class begins.</p>
<p class="note"><em>To make the presentation clear and short, our code examples ignore errors and possible security issues. The purpose is to show how jQuery and Ajax techniques can improve an old script. For production use, you would check function error returns, untaint input data, and follow the other rules of good web hygiene. With the new Ajax methods, an error in the client or server code can cause a silent failure. <a href="http://www.getfirebug.com/">FireBug</a> is a very handy tool for developing and debugging Ajax applications. The full-featured version is a Firefox plugin, but a light version is available for IE and other browsers.</em></p>
<p>Let&#39;s define our form&#39;s requirements:</p>
<ol>
<li>Get data from a <code>people</code> table in a database. The <code>id</code> column is the primary key.</li>
<li>Display the names in a pull-down menu (a form <code>select</code> element).</li>
<li>Let the user select a person from the menu.</li>
<li>Display information about that person in a table: first name, last name, favorite dance, and favorite pie.</li>
</ol>
<p>This example assumes the number of people will fit in an HTML <code>select</code> element without killing the browser. Larger data would require a paged table or something similar. The page should look something like this:</p>
<hr size="1" />
<p>People</p>
<select name="select">
<option value="">(please select a person)</option>
<option selected="selected" value="1">Alfredo de Darc</option>
<option value="2">Ransom Duxover</option>
<option value="3">Creighton Barrel</option>
<option value="4">Papa Gasquette</option>
<option value="5">Bob Frapples</option>
</select>
<table border="1">
<tbody>
<tr>
<td>First Name</td>
<td>First Name</td>
<td>Dance</td>
<td>Pie</td>
</tr>
<tr>
<td>Alfredo</td>
<td>de Darc</td>
<td>tango</td>
<td>blueberry</td>
</tr>
</tbody>
</table>
<hr size="1" />
<h4>Version 1: Original Code</h4>
<p>In the original version we do everything in a single PHP script: write the static HTML, create the original list of people, and fill in the lower table if a person had been selected.</p>
<pre><strong>people1.php:</strong>
&lt;?php
$cmd    = @$_REQUEST[&quot;cmd&quot;];
$id     = @$_REQUEST[&quot;id&quot;];
mysql_connect(<em>$server</em>, <em>$user</em>, <em>$password</em>);
mysql_select_db(&quot;test&quot;);
?&gt;
&lt;html&gt;
&lt;head&gt;&lt;title&gt;Old Form&lt;/title&gt;
&lt;script&gt;
// Get the selected user and retrieve his/her info
function user_info(sel)
        {
        var opt    = sel.options;
        var user_id    = opt[sel.selectedIndex].value;
        // Construct a GET URL, or create a hidden field for &quot;cmd&quot;
        var url  =  &quot;people1.php?cmd=info&amp;id=&quot; + user_id;
        window.location.href = url;
        }
&lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;form action=&quot;people1.php&quot; method=&quot;post&quot;&gt;
People&lt;br&gt;
&lt;select name=&quot;people&quot; onchange=&quot;user_info(this)&quot;&gt;
&lt;option value=&quot;&quot;&gt;(select a person)
&lt;?php
// Get all users and display every time script is called
$result = mysql_query(&quot;select id, fname, lname from people&quot;);
while ($row = mysql_fetch_array($result, MYSQL_ASSOC))
        echo &quot;&lt;option value=&#39;$row[id]&#39;&quot;,
                $row[&quot;id&quot;] == $id ? &quot; selected&quot; : &quot;&quot;,
                &quot;&gt;$row[fname] $row[lname]\n&quot;;
?&gt;
&lt;/select&gt;
&lt;?php
if ($cmd == &quot;info&quot;)
        {
    $id     = mysql_real_escape_string($id);
        $result = mysql_query(&quot;select * from people where id=&#39;$id&#39;&quot;);
        $info = mysql_fetch_array($result, MYSQL_ASSOC);
        }
else
        $info = array(&quot;fname&quot;=&gt;&quot; &quot;, &quot;lname&quot;=&gt;&quot; &quot;, &quot;dance&quot;=&gt;&quot; &quot;, &quot;pie&quot;=&gt;&quot; &quot;);
echo &lt;&lt;&lt; END
&lt;br&gt;
&lt;table border=1&gt;
&lt;tr&gt;
&lt;td&gt;First Name&lt;/td&gt;&lt;td&gt;Last Name&lt;/td&gt;&lt;td&gt;Dance&lt;/td&gt;&lt;td&gt;Pie&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;$info[fname]&lt;/td&gt;
&lt;td&gt;$info[lname]&lt;/td&gt;
&lt;td&gt;$info[dance]&lt;/td&gt;
&lt;td&gt;$info[pie]&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;

END;
?&gt;
&lt;/form&gt;
&lt;/body&gt;
</pre>
<h4>New Design</h4>
<p>Here&#39;s the plan:</p>
<ol>
<li>Split the initial script into three files: static content (HTML), client-side processing (JavaScript), and server-side processing (PHP).</li>
<li>Include <em>jquery.js</em> and the new JavaScript file in the HTML file.</li>
<li>Add a unique <code>id</code> attributes to all dynamic content tags.</li>
<li>Define JavaScript functions to make Ajax-style server calls and update page elements.</li>
</ol>
<p>We can merge the dynamic and static content in one of two ways:</p>
<ul>
<li>PHP returns the dynamic content as HTML, and JavaScript stuffs it into a page element with <code>innerHTML</code>.</li>
<li>PHP returns the dynamic content in a JSON-format data array, and JavaScript builds and inserts the HTML into the target page element.</li>
</ul>
<p>Let&#39;s try both.</p>
<h4>Version 2: Ajax Submit, HTML Return</h4>
<p>In this method, PHP generates the HTML for the <code>option</code> tags, and we just stuff the HTML into the enclosing <code>select</code>. Now we start with an HTML file, and it&#39;s just a container:</p>
<pre><strong>people2.html</strong>:
&lt;html&gt;
&lt;head&gt;&lt;title&gt;New Form Version 1&lt;/title&gt;
&lt;script src=&quot;jquery.js&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;people2.js&quot;&gt;&lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;
People&lt;br&gt;
&lt;select id=&quot;people&quot;&gt;
&lt;/select&gt;
&lt;br&gt;
&lt;table border=1&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;td&gt;First Name&lt;/td&gt;&lt;td&gt;Last Name&lt;/td&gt;&lt;td&gt;Dance&lt;/td&gt;&lt;td&gt;Pie&lt;/td&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody id=&quot;info&quot;&gt;
&lt;tr&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/body&gt;
&lt;/html&gt;</pre>
<p>Now let&#39;s look at the JavaScript file. jQuery&#39;s central function is <code>$()</code>, which returns a jQuery object. Its arguments may have many forms, but we&#39;ll use these now:</p>
<ul>
<li><code>$(document)</code>: Selects the DOM <code>document</code> object.</li>
<li><code>$(&quot;#myid&quot;)</code>: Selects the element with <code>id=&quot;myid&quot;</code>.</li>
</ul>
<p>The jQuery call <code>$(document).ready</code> replaces <code>window.onload</code>. It&#39;s called when the DOM is ready, instead of waiting for all images to load. This avoids synchronization problems. All of the JavaScript functions that work with form elements can be in here.</p>
<p>The <code>load()</code> function calls the server script <code>people.php</code> and inserts its output into the <code>select</code> element with id <code>people</code>. This is the equivalent of the first chunk of the original version. It makes an Ajax connection to the server, and inserts the returned HTML into the element with id <code>people</code>:</p>
<p>(I&#39;ve spread out the formatting to help distinguish the parentheses and curly brackets.)</p>
<pre><strong>people2.js:</strong>
$(document).ready
    (
    function()
        {
        // Call this when the DOM is ready:
        $(&quot;#people&quot;).load(&quot;people2.php?cmd=init&quot;);
        // Call this when a person is selected:
        $(&quot;#people&quot;).change(function()
            {
            // get the user&#39;s id from the selected option:
            var user_id = $(&quot;:selected&quot;).val();
            $(&quot;#info&quot;).load(&quot;people2.php?cmd=info&amp;id=&quot; + user_id);
            });
        }
    );</pre>
<p>For this version, that&#39;s all the JavaScript we need. By the way, this approach has been called <a href="http://microformats.org/wiki/rest/ahah">AHAH</a> (Asynchronous HTML and HTTP). Now we&#39;ll look at the PHP script it calls. This version is like the original, but it only prints the HTML fragment for the current query:</p>
<pre><strong>people2.php:</strong>
  &lt;?php

  $cmd    = @$_REQUEST[&quot;cmd&quot;];
  $id    = @$_REQUEST[&quot;id&quot;];
  mysql_connect(<em>$server</em>, <em>$user</em>, <em>$password</em>);
mysql_select_db(&quot;test&quot;);
if ($cmd == &quot;init&quot;)
        {
        $result = mysql_query(&quot;select id, fname, lname from people&quot;);
        echo &quot;&lt;option value=&#39;&#39;&gt;(select a person)\n&quot;;
        while ($row = mysql_fetch_array($result, MYSQL_ASSOC))
                echo &quot;&lt;option value=&#39;$row[id]&#39;&gt;$row[fname] $row[lname]\n&quot;;
        }
elseif ($cmd == &quot;info&quot;)
        {
        $id     = mysql_real_escape_string($id);
        $result = mysql_query(&quot;select * from people where id=&#39;$id&#39;&quot;);
        $info = mysql_fetch_array($result, MYSQL_ASSOC);
        echo &lt;&lt;&lt; END
&lt;tr&gt;
&lt;td&gt;$info[fname]&lt;/td&gt;
&lt;td&gt;$info[lname]&lt;/td&gt;
&lt;td&gt;$info[dance]&lt;/td&gt;
&lt;td&gt;$info[pie]&lt;/td&gt;
&lt;/tr&gt;

END;
        }
?&gt;</pre>
<p>Although you could call this approach AJAJ, thankfully no one does.</p>
<h4>Version 3: Ajax Submit, JSON Return</h4>
<p>In this alternative, PHP builds a data array from the database query and returns it to JavaScript in JSON format. jQuery converts this JSON string into a jQuery object and passes it to a callback function, which builds the HTML for the options and inserts it into the appropriate page element. This example is the same as <code>people2.html</code>, except it calls <code>people3.js</code>:</p>
<pre><strong>people3.html:</strong>
&lt;html&gt;
&lt;head&gt;&lt;title&gt;New Form Version 1&lt;/title&gt;
&lt;script src=&quot;jquery.js&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;people3.js&quot;&gt;&lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;
People&lt;br&gt;
&lt;select id=&quot;people&quot;&gt;
&lt;/select&gt;
&lt;br&gt;
&lt;table border=1&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;td&gt;First Name&lt;/td&gt;&lt;td&gt;Last Name&lt;/td&gt;&lt;td&gt;Dance&lt;/td&gt;&lt;td&gt;Pie&lt;/td&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody id=&quot;info&quot;&gt;
&lt;tr&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/body&gt;
&lt;/html&gt;</pre>
<p>We&#39;re just going to fill the container in a different way. In this version, we call <code>getJSON</code> with three arguments:</p>
<ul>
<li>The URL</li>
<li>A dictionary of name:value pairs</li>
<li>A JavaScript callback function.</li>
</ul>
<p>A GET URL will be built from the URL and name:value arguments, and the returned JSON string will be converted into a JavaScript object and passed to the callback function.</p>
<pre><strong>people3.js:</strong>
 $(document).ready
        (
        function()
                {
        // Call this when the DOM is ready:
                $.getJSON(&quot;people3.php&quot;,
                        { cmd : &quot;init&quot; },
                        make_menu);
        // Call this when a person is selected:
                $(&quot;#people&quot;).change(function()
                        {
                        var user_id = $(&quot;:selected&quot;).val();
                        $.getJSON(&quot;people3.php&quot;,
                                { cmd : &quot;info&quot;, id: user_id  },
                                make_info);
                        });
                }
        );
function make_menu(obj)
        {
        var str = &quot;&quot;;
        var len = obj.length;
        str += &quot;&lt;option value=&#39;&#39;&gt;(select a person)\n&quot;;
        for (var i = 0; i &lt; len; i++)
                {
                var user = obj[i];
                str += &quot;&lt;option value=&#39;&quot; + user[&quot;id&quot;] + &quot;&#39;&gt;&quot; +
                        user[&quot;fname&quot;] + &quot; &quot; +
                        user[&quot;lname&quot;] + &quot;\n&quot;;
                }
        $(&quot;#people&quot;).html(str);
        }
function make_info(info)
        {
        var str = &quot;&lt;tr&gt;&quot;;
    // You can get each value as info.<em>name</em> or info[&quot;<em>name</em>&quot;].
    // Let&#39;s get the first name using the first way.
        str += &quot;&lt;td&gt;&quot; + info.fname + &quot;&lt;/td&gt;&quot;;
        str += &quot;&lt;td&gt;&quot; + info[&quot;lname&quot;] + &quot;&lt;/td&gt;&quot;;
        str += &quot;&lt;td&gt;&quot; + info[&quot;dance&quot;] + &quot;&lt;/td&gt;&quot;;
        str += &quot;&lt;td&gt;&quot; + info[&quot;pie&quot;] + &quot;&lt;/td&gt;&quot;;
        str += &quot;&lt;/tr&gt;\n&quot;;
        $(&quot;#info&quot;).html(str);
        }</pre>
<p>This version of the PHP script is even simpler than <code>people2.php</code>. Instead of creating HTML from the database return values, we just encode the PHP data array in JSON format and send it off:</p>
<pre><strong>people3.php:</strong>
&lt;?php
$cmd    = @$_REQUEST[&quot;cmd&quot;];
$id     = @$_REQUEST[&quot;id&quot;];
mysql_connect(<em>$server</em>, <em>$user</em>, <em>$password</em>);
mysql_select_db(&quot;test&quot;);
if ($cmd == &quot;init&quot;)
        {
        $result = mysql_query(&quot;select id, fname, lname from people&quot;);
        $user_array = array();
        while($row = mysql_fetch_array($result, MYSQL_ASSOC))
                $user_array[] = $row;
        echo json_encode($user_array);
        }
elseif ($cmd == &quot;info&quot;)
        {
        $id = mysql_real_escape_string($id);
        $result = mysql_query(&quot;select * from people where id=&#39;$id&#39;&quot;);
        $info = mysql_fetch_array($result, MYSQL_ASSOC);
        echo json_encode($info);
        }
?&gt;</pre>
<p>The <code>json_encode</code> function is included with standard PHP starting with version 5.2. For earlier versions, see the PHP JSON <a href="http://php.net/json">manual section</a>.</p>
<p>If you chose our friend Alfredo from the menu, the JSON returned would look like this:</p>
<pre>{&quot;id&quot;:&quot;1&quot;,&quot;fname&quot;:&quot;Alfredo&quot;,&quot;lname&quot;:&quot;de Darc&quot;,&quot;dance&quot;:&quot;tango&quot;,&quot;pie&quot;:&quot;blueberry&quot;}</pre>
<p>That JSON string is converted to a JavaScript object by jQuery and passed to the <code>make_menu</code> function as the <code>info</code> argument.</p>
<h4>Judging the Makeover</h4>
<p>The main choice between these new versions is where to do the output formatting: in PHP (version 2) or in JavaScript (version 3). Another factor might be what other plans you have for the data. Instead of throwing <code>info</code> away after generating the HTML in version 3, you could save it in a global JavaScript variable and use it for other purposes later.</p>
<p>Are these new versions better than the original? Let&#39;s see if our original promises were kept:</p>
<dl>
<dt>Separating dynamic content from static content.</dt>
<dd>All the static content is in the HTML file, and the dynamic data from the PHP script are processed in JavaScript for page placement.</dd>
<dt>&nbsp;</dt>
<dt>Separating content, style, and processing.</dt>
<dd>We didn&#39;t show it here, but a separate CSS file would be a nice orthogonal addition.</dd>
<dt>&nbsp;</dt>
<dt>Web client-server communication via function calls.</dt>
<dd>Good old Ajax.</dd>
<dt>&nbsp;</dt>
<dt>Partial page updates instead of flash-bang page reloads.</dt>
<dd>Ajax again.</dd>
<dt>&nbsp;</dt>
<dt>Faster development and more maintainable code.</dt>
<dd>PHP now just gets data from the database and returns output chunks (HTML or JSON) rather than whole pages.</dd>
<dt>&nbsp;</dt>
<dt>Faster load times and improved caching.</dt>
<dd><code>people2.html</code>, <code>people2.js</code>, <code>people3.html</code>, and <code>people3.js</code> are static files that will be cached by the browser (and the web server, which will make the application more scalable). The whole output page is also cached, the only changes being performed by JavaScript within the browser. Finally, we avoid a database lookup in every call to <code>people2.php</code> or <code>people3.php</code> after the first one.</dd>
</dl>
<p>The most important benefit is that the new approach will scale much better with future requirements, such as adding a new person or editing the data of an existing person. And new requirements are as sure as death and taxes.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.allfreetech.com/php/migrating-web-based-php-applications-to-ajax-46.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Creating Sortable Lists With PHP And Ajax</title>
		<link>http://www.allfreetech.com/php/creating-sortable-lists-with-php-and-ajax-42.html</link>
		<comments>http://www.allfreetech.com/php/creating-sortable-lists-with-php-and-ajax-42.html#comments</comments>
		<pubDate>Sat, 23 Jan 2010 03:04:00 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Ajax]]></category>

		<guid isPermaLink="false">http://www.allfreetech.com/?p=42</guid>
		<description><![CDATA[You might have been in a situation before where you had a list of items in your database that needed to be output in a specific order. These items could be anything: perhaps a listing of your favourite movies or your favourite books. For whatever reason, you want them ordered in a custom way that [...]]]></description>
			<content:encoded><![CDATA[<p>You might have been in a situation before where you had a list of items in your database that needed to be output in a specific order. These items could be anything: perhaps a listing of your favourite movies or your favourite books. For whatever reason, you want them ordered in a custom way that can&rsquo;t be determined automatically (such as alphabetical).<span id="more-42"></span></p>
<p>This article covers the implementation of a system that lets you easily define the order of such a list.</p>
<p>Traditionally, implementations of such functionality involve you clicking a &ldquo;move up&rdquo;, &ldquo;move down&rdquo;, &ldquo;move to top&rdquo;, or &ldquo;move to bottom&rdquo; button that switches the order the items (one item at a time). Or perhaps each item has a text box with a number in it, that by changing the numbers you can change the order of the list.</p>
<p>In any case, these methods are much more difficult to use than they should be. In this article, we&rsquo;ll create a drag drop system using JavaScript that will let you drag an item to its new position, and then save the new order as soon as you drop the item.</p>
<p>To achieve the Ajax effects (that is, the drag/drop effect, and the seamless saving of ordering data), we will be using the Prototype and Scriptaculous libraries.</p>
<p>Firstly, we will create a database table (compatible with MySQL and PostgreSQL) and populate it with data. Then we will output our list and apply the drag and drop effects to it. Finally, we will deal with the saving of the new ordering data.</p>
<p>For our example, we will use a list of &ldquo;favourite movies&rdquo;, and implement functionality to change the order of our movies.</p>
<h2>Creating Our Database And Populating It</h2>
<p>We will now create the database table we need in order to create this example. We won&rsquo;t be writing all the code for inserting, editing and deleting of data, as it is beyond the scope of this example. As such, we will simply provide insert statements to create a static list of data.</p>
<p>The examples below are for PostgreSQL and MySQL.</p>
<h3>Create your database</h3>
<p>First up, you need to create a database for this article. This may be in either PostgreSQL or MySQL. Additionally, you may need to setup a username and password to access the database, depending on your system setup.</p>
<h3>MySQL database schema</h3>
<div class="listing">
<div class="caption"><strong>Listing 1</strong> listing-1.sql</div>
<div class="highlight">
<div class="hl-main">
<pre><span class="hl-reserved">create</span><span class="hl-code"> </span><span class="hl-reserved">table</span><span class="hl-code"> </span><span class="hl-identifier">movies</span><span class="hl-code"> </span><span class="hl-brackets">(</span>
<span class="hl-code">    </span><span class="hl-identifier">movie_id</span><span class="hl-code">    </span><span class="hl-reserved">int</span><span class="hl-code">             </span><span class="hl-reserved">not</span><span class="hl-code"> </span><span class="hl-reserved">null</span><span class="hl-code">    </span><span class="hl-identifier">auto_increment</span><span class="hl-code">,
    </span><span class="hl-identifier">title</span><span class="hl-code">       </span><span class="hl-reserved">varchar</span><span class="hl-brackets">(</span><span class="hl-number">255</span><span class="hl-brackets">)</span><span class="hl-code">    </span><span class="hl-reserved">not</span><span class="hl-code"> </span><span class="hl-reserved">null</span><span class="hl-code">,
    </span><span class="hl-identifier">ranking</span><span class="hl-code">     </span><span class="hl-reserved">int</span><span class="hl-code">,

    </span><span class="hl-reserved">primary</span><span class="hl-code"> </span><span class="hl-reserved">key</span><span class="hl-code"> </span><span class="hl-brackets">(</span><span class="hl-identifier">movie_id</span><span class="hl-brackets">)</span>
<span class="hl-brackets">)</span><span class="hl-code">;</span></pre>
</div></div>
</div>
<h3>PostgreSQL database schema</h3>
<div class="listing">
<div class="caption"><strong>Listing 2</strong> listing-2.sql</div>
<div class="highlight">
<div class="hl-main">
<pre><span class="hl-reserved">create</span><span class="hl-code"> </span><span class="hl-reserved">table</span><span class="hl-code"> </span><span class="hl-identifier">movies</span><span class="hl-code"> </span><span class="hl-brackets">(</span>
<span class="hl-code">    </span><span class="hl-identifier">movie_id</span><span class="hl-code">    </span><span class="hl-identifier">serial</span><span class="hl-code">          </span><span class="hl-reserved">not</span><span class="hl-code"> </span><span class="hl-reserved">null</span><span class="hl-code">,
    </span><span class="hl-identifier">title</span><span class="hl-code">       </span><span class="hl-reserved">varchar</span><span class="hl-brackets">(</span><span class="hl-number">255</span><span class="hl-brackets">)</span><span class="hl-code">    </span><span class="hl-reserved">not</span><span class="hl-code"> </span><span class="hl-reserved">null</span><span class="hl-code">,
    </span><span class="hl-identifier">ranking</span><span class="hl-code">     </span><span class="hl-reserved">int</span><span class="hl-code">,

    </span><span class="hl-reserved">primary</span><span class="hl-code"> </span><span class="hl-reserved">key</span><span class="hl-code"> </span><span class="hl-brackets">(</span><span class="hl-identifier">movie_id</span><span class="hl-brackets">)</span>
<span class="hl-brackets">)</span><span class="hl-code">;</span></pre>
</div></div>
</div>
<h3>Database data</h3>
<div class="listing">
<div class="caption"><strong>Listing 3</strong> listing-3.sql</div>
<div class="highlight">
<div class="hl-main">
<pre><span class="hl-reserved">insert</span><span class="hl-code"> </span><span class="hl-reserved">into</span><span class="hl-code"> </span><span class="hl-identifier">movies</span><span class="hl-code"> </span><span class="hl-brackets">(</span><span class="hl-identifier">title</span><span class="hl-brackets">)</span><span class="hl-code"> </span><span class="hl-reserved">values</span><span class="hl-code"> </span><span class="hl-brackets">(</span><span class="hl-quotes">&#39;</span><span class="hl-string">American Pie</span><span class="hl-quotes">&#39;</span><span class="hl-brackets">)</span><span class="hl-code">;
</span><span class="hl-reserved">insert</span><span class="hl-code"> </span><span class="hl-reserved">into</span><span class="hl-code"> </span><span class="hl-identifier">movies</span><span class="hl-code"> </span><span class="hl-brackets">(</span><span class="hl-identifier">title</span><span class="hl-brackets">)</span><span class="hl-code"> </span><span class="hl-reserved">values</span><span class="hl-code"> </span><span class="hl-brackets">(</span><span class="hl-quotes">&#39;</span><span class="hl-string">Die Hard</span><span class="hl-quotes">&#39;</span><span class="hl-brackets">)</span><span class="hl-code">;
</span><span class="hl-reserved">insert</span><span class="hl-code"> </span><span class="hl-reserved">into</span><span class="hl-code"> </span><span class="hl-identifier">movies</span><span class="hl-code"> </span><span class="hl-brackets">(</span><span class="hl-identifier">title</span><span class="hl-brackets">)</span><span class="hl-code"> </span><span class="hl-reserved">values</span><span class="hl-code"> </span><span class="hl-brackets">(</span><span class="hl-quotes">&#39;</span><span class="hl-string">Clerks</span><span class="hl-quotes">&#39;</span><span class="hl-brackets">)</span><span class="hl-code">;
</span><span class="hl-reserved">insert</span><span class="hl-code"> </span><span class="hl-reserved">into</span><span class="hl-code"> </span><span class="hl-identifier">movies</span><span class="hl-code"> </span><span class="hl-brackets">(</span><span class="hl-identifier">title</span><span class="hl-brackets">)</span><span class="hl-code"> </span><span class="hl-reserved">values</span><span class="hl-code"> </span><span class="hl-brackets">(</span><span class="hl-quotes">&#39;</span><span class="hl-string">Air Force One</span><span class="hl-quotes">&#39;</span><span class="hl-brackets">)</span><span class="hl-code">;
</span><span class="hl-reserved">insert</span><span class="hl-code"> </span><span class="hl-reserved">into</span><span class="hl-code"> </span><span class="hl-identifier">movies</span><span class="hl-code"> </span><span class="hl-brackets">(</span><span class="hl-identifier">title</span><span class="hl-brackets">)</span><span class="hl-code"> </span><span class="hl-reserved">values</span><span class="hl-code"> </span><span class="hl-brackets">(</span><span class="hl-quotes">&#39;</span><span class="hl-string">Titanic</span><span class="hl-quotes">&#39;</span><span class="hl-brackets">)</span><span class="hl-code">;
</span><span class="hl-reserved">insert</span><span class="hl-code"> </span><span class="hl-reserved">into</span><span class="hl-code"> </span><span class="hl-identifier">movies</span><span class="hl-code"> </span><span class="hl-brackets">(</span><span class="hl-identifier">title</span><span class="hl-brackets">)</span><span class="hl-code"> </span><span class="hl-reserved">values</span><span class="hl-code"> </span><span class="hl-brackets">(</span><span class="hl-quotes">&#39;</span><span class="hl-string">The Shawshank Redemption</span><span class="hl-quotes">&#39;</span><span class="hl-brackets">)</span><span class="hl-code">;
</span><span class="hl-reserved">insert</span><span class="hl-code"> </span><span class="hl-reserved">into</span><span class="hl-code"> </span><span class="hl-identifier">movies</span><span class="hl-code"> </span><span class="hl-brackets">(</span><span class="hl-identifier">title</span><span class="hl-brackets">)</span><span class="hl-code"> </span><span class="hl-reserved">values</span><span class="hl-code"> </span><span class="hl-brackets">(</span><span class="hl-quotes">&#39;</span><span class="hl-string">Gone In 60 Seconds</span><span class="hl-quotes">&#39;</span><span class="hl-brackets">)</span><span class="hl-code">;</span></pre>
</div></div>
</div>
<h3>About the schema</h3>
<p>The database table is fairly simple, it just consists of an ID, a movie title, and a field to store the ordering. There&rsquo;s no particular reason why the ranking field is allowed to be null, other than the values won&rsquo;t be set when we initially insert our data.</p>
<p>If we were being really tricky, we would write a trigger on the database that would assign the next ranking value when a row is inserted, but that is beyond the scope of this article.</p>
<h2>Outputting The Database Data</h2>
<p>Now that we&rsquo;ve made and populated our database, we&rsquo;re going to write a <span class="caps">PHP</span> script to connect to this database and select all of this data. Because we are making both a MySQL version and a PostgreSQL version, some of this code will be implemented twice (once for each).</p>
<p>Hopefully you are using database abstraction in your web applications, but for the purpose of this article we&rsquo;ll assume that you aren&rsquo;t.</p>
<h3>database.php for MySQL</h3>
<div class="listing">
<div class="caption"><strong>Listing 4</strong> database.php</div>
<div class="highlight">
<div class="hl-main">
<pre><span class="hl-inlinetags">&lt;?php</span>
<span class="hl-code">    </span><span class="hl-reserved">function</span><span class="hl-code"> </span><span class="hl-identifier">dbConnect</span><span class="hl-brackets">()</span>
<span class="hl-code">    </span><span class="hl-brackets">{</span>
<span class="hl-code">        </span><span class="hl-var">$link</span><span class="hl-code"> = </span><span class="hl-identifier">mysql_connect</span><span class="hl-brackets">(</span><span class="hl-quotes">&#39;</span><span class="hl-string">localhost</span><span class="hl-quotes">&#39;</span><span class="hl-code">, </span><span class="hl-quotes">&#39;</span><span class="hl-string">username</span><span class="hl-quotes">&#39;</span><span class="hl-code">, </span><span class="hl-quotes">&#39;</span><span class="hl-string">password</span><span class="hl-quotes">&#39;</span><span class="hl-brackets">)</span><span class="hl-code">;
        </span><span class="hl-reserved">if</span><span class="hl-code"> </span><span class="hl-brackets">(</span><span class="hl-code">!</span><span class="hl-var">$link</span><span class="hl-brackets">)</span>
<span class="hl-code">            </span><span class="hl-reserved">return</span><span class="hl-code"> </span><span class="hl-reserved">false</span><span class="hl-code">;

        </span><span class="hl-reserved">return</span><span class="hl-code"> </span><span class="hl-identifier">mysql_select_db</span><span class="hl-brackets">(</span><span class="hl-quotes">&#39;</span><span class="hl-string">phpriot</span><span class="hl-quotes">&#39;</span><span class="hl-brackets">)</span><span class="hl-code">;
    </span><span class="hl-brackets">}</span>
<span class="hl-inlinetags">?&gt;</span></pre>
</div></div>
</div>
<h3>database.php for PostgreSQL</h3>
<div class="listing">
<div class="caption"><strong>Listing 5</strong> database.php</div>
<div class="highlight">
<div class="hl-main">
<pre><span class="hl-inlinetags">&lt;?php</span>
<span class="hl-code">    </span><span class="hl-reserved">function</span><span class="hl-code"> </span><span class="hl-identifier">dbConnect</span><span class="hl-brackets">()</span>
<span class="hl-code">    </span><span class="hl-brackets">{</span>
<span class="hl-code">        </span><span class="hl-var">$str</span><span class="hl-code"> = </span><span class="hl-identifier">sprintf</span><span class="hl-brackets">(</span><span class="hl-quotes">&#39;</span><span class="hl-string">host=%s user=%s password=%s dbname=%s</span><span class="hl-quotes">&#39;</span><span class="hl-code">,
                       </span><span class="hl-quotes">&#39;</span><span class="hl-string">localhost</span><span class="hl-quotes">&#39;</span><span class="hl-code">,
                       </span><span class="hl-quotes">&#39;</span><span class="hl-string">username</span><span class="hl-quotes">&#39;</span><span class="hl-code">,
                       </span><span class="hl-quotes">&#39;</span><span class="hl-string">password</span><span class="hl-quotes">&#39;</span><span class="hl-code">,
                       </span><span class="hl-quotes">&#39;</span><span class="hl-string">phpriot</span><span class="hl-quotes">&#39;</span><span class="hl-brackets">)</span><span class="hl-code">;
        </span><span class="hl-var">$link</span><span class="hl-code"> = </span><span class="hl-identifier">pg_connect</span><span class="hl-brackets">(</span><span class="hl-var">$str</span><span class="hl-brackets">)</span><span class="hl-code">;

        </span><span class="hl-reserved">return</span><span class="hl-code"> </span><span class="hl-brackets">(</span><span class="hl-identifier">bool</span><span class="hl-brackets">)</span><span class="hl-code"> </span><span class="hl-var">$link</span><span class="hl-code">;
    </span><span class="hl-brackets">}</span>
<span class="hl-inlinetags">?&gt;</span></pre>
</div></div>
</div>
<h3>movies.php for MySQL</h3>
<div class="listing">
<div class="caption"><strong>Listing 6</strong> movies.php</div>
<div class="highlight">
<div class="hl-main">
<pre><span class="hl-inlinetags">&lt;?php</span>
<span class="hl-code">    </span><span class="hl-reserved">function</span><span class="hl-code"> </span><span class="hl-identifier">getMovies</span><span class="hl-brackets">()</span>
<span class="hl-code">    </span><span class="hl-brackets">{</span>
<span class="hl-code">        </span><span class="hl-var">$query</span><span class="hl-code"> = </span><span class="hl-quotes">&#39;</span><span class="hl-string">select movie_id, title from movies order by ranking, lower(title)</span><span class="hl-quotes">&#39;</span><span class="hl-code">;
        </span><span class="hl-var">$result</span><span class="hl-code"> = </span><span class="hl-identifier">mysql_query</span><span class="hl-brackets">(</span><span class="hl-var">$query</span><span class="hl-brackets">)</span><span class="hl-code">;

        </span><span class="hl-var">$movies</span><span class="hl-code"> = </span><span class="hl-reserved">array</span><span class="hl-brackets">()</span><span class="hl-code">;
        </span><span class="hl-reserved">while</span><span class="hl-code"> </span><span class="hl-brackets">(</span><span class="hl-var">$row</span><span class="hl-code"> = </span><span class="hl-identifier">mysql_fetch_object</span><span class="hl-brackets">(</span><span class="hl-var">$result</span><span class="hl-brackets">))</span><span class="hl-code"> </span><span class="hl-brackets">{</span>
<span class="hl-code">            </span><span class="hl-var">$movies</span><span class="hl-brackets">[</span><span class="hl-var">$row</span><span class="hl-code">-&gt;</span><span class="hl-identifier">movie_id</span><span class="hl-brackets">]</span><span class="hl-code"> = </span><span class="hl-var">$row</span><span class="hl-code">-&gt;</span><span class="hl-identifier">title</span><span class="hl-code">;
        </span><span class="hl-brackets">}</span>
<span class="hl-code">
        </span><span class="hl-reserved">return</span><span class="hl-code"> </span><span class="hl-var">$movies</span><span class="hl-code">;
    </span><span class="hl-brackets">}</span>
<span class="hl-inlinetags">?&gt;</span></pre>
</div></div>
</div>
<h3>movies.php for PostgreSQL</h3>
<div class="listing">
<div class="caption"><strong>Listing 7</strong> movies.php</div>
<div class="highlight">
<div class="hl-main">
<pre><span class="hl-inlinetags">&lt;?php</span>
<span class="hl-code">    </span><span class="hl-reserved">function</span><span class="hl-code"> </span><span class="hl-identifier">getMovies</span><span class="hl-brackets">()</span>
<span class="hl-code">    </span><span class="hl-brackets">{</span>
<span class="hl-code">        </span><span class="hl-var">$query</span><span class="hl-code"> = </span><span class="hl-quotes">&#39;</span><span class="hl-string">select movie_id, title from movies order by ranking, lower(title)</span><span class="hl-quotes">&#39;</span><span class="hl-code">;
        </span><span class="hl-var">$result</span><span class="hl-code"> = </span><span class="hl-identifier">pg_query</span><span class="hl-brackets">(</span><span class="hl-var">$query</span><span class="hl-brackets">)</span><span class="hl-code">;

        </span><span class="hl-var">$movies</span><span class="hl-code"> = </span><span class="hl-reserved">array</span><span class="hl-brackets">()</span><span class="hl-code">;
        </span><span class="hl-reserved">while</span><span class="hl-code"> </span><span class="hl-brackets">(</span><span class="hl-var">$row</span><span class="hl-code"> = </span><span class="hl-identifier">pg_fetch_object</span><span class="hl-brackets">(</span><span class="hl-var">$result</span><span class="hl-brackets">))</span><span class="hl-code"> </span><span class="hl-brackets">{</span>
<span class="hl-code">            </span><span class="hl-var">$movies</span><span class="hl-brackets">[</span><span class="hl-var">$row</span><span class="hl-code">-&gt;</span><span class="hl-identifier">movie_id</span><span class="hl-brackets">]</span><span class="hl-code"> = </span><span class="hl-var">$row</span><span class="hl-code">-&gt;</span><span class="hl-identifier">title</span><span class="hl-code">;
        </span><span class="hl-brackets">}</span>
<span class="hl-code">
        </span><span class="hl-reserved">return</span><span class="hl-code"> </span><span class="hl-var">$movies</span><span class="hl-code">;
    </span><span class="hl-brackets">}</span>
<span class="hl-inlinetags">?&gt;</span></pre>
</div></div>
</div>
<h3>index.php for MySQL and PostgreSQL</h3>
<p>Here&rsquo;s the main script that displays the list of movies. It is the same for both MySQL and PostgreSQL, as it will include the necessary code. At this point it is not styled and it is not yet possible to change the ordering. We&rsquo;ll be adding each of those things in next.</p>
<div class="listing">
<div class="caption"><strong>Listing 8</strong> index.php</div>
<div class="highlight">
<div class="hl-main">
<pre><span class="hl-inlinetags">&lt;?php</span>
<span class="hl-code">    </span><span class="hl-reserved">require_once</span><span class="hl-brackets">(</span><span class="hl-quotes">&#39;</span><span class="hl-string">database.php</span><span class="hl-quotes">&#39;</span><span class="hl-brackets">)</span><span class="hl-code">;
    </span><span class="hl-reserved">require_once</span><span class="hl-brackets">(</span><span class="hl-quotes">&#39;</span><span class="hl-string">movies.php</span><span class="hl-quotes">&#39;</span><span class="hl-brackets">)</span><span class="hl-code">;

    </span><span class="hl-reserved">if</span><span class="hl-code"> </span><span class="hl-brackets">(</span><span class="hl-code">!</span><span class="hl-identifier">dbConnect</span><span class="hl-brackets">())</span><span class="hl-code"> </span><span class="hl-brackets">{</span>
<span class="hl-code">        </span><span class="hl-reserved">echo</span><span class="hl-code"> </span><span class="hl-quotes">&#39;</span><span class="hl-string">Error connecting to database</span><span class="hl-quotes">&#39;</span><span class="hl-code">;
        </span><span class="hl-reserved">exit</span><span class="hl-code">;
    </span><span class="hl-brackets">}</span>
<span class="hl-code">
    </span><span class="hl-var">$movies</span><span class="hl-code"> = </span><span class="hl-identifier">getMovies</span><span class="hl-brackets">()</span><span class="hl-code">;
</span><span class="hl-inlinetags">?&gt;</span>
<span class="hl-code">&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Strict//EN&quot; &quot;DTD/xhtml1-strict.dtd&quot;&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;phpRiot Sortable Lists&lt;/title&gt;
    &lt;/head&gt;
    &lt;body&gt;
        &lt;h1&gt;phpRiot Sortable Lists&lt;/h1&gt;

        &lt;ul id=&quot;movies_list&quot;&gt;
            </span><span class="hl-inlinetags">&lt;?php</span><span class="hl-code"> </span><span class="hl-reserved">foreach</span><span class="hl-code"> </span><span class="hl-brackets">(</span><span class="hl-var">$movies</span><span class="hl-code"> </span><span class="hl-reserved">as</span><span class="hl-code"> </span><span class="hl-var">$movie_id</span><span class="hl-code"> =&gt; </span><span class="hl-var">$title</span><span class="hl-brackets">)</span><span class="hl-code"> </span><span class="hl-brackets">{</span><span class="hl-code"> </span><span class="hl-inlinetags">?&gt;</span>
<span class="hl-default">                &lt;li&gt;</span><span class="hl-inlinetags">&lt;?=</span><span class="hl-code"> </span><span class="hl-var">$title</span><span class="hl-code"> </span><span class="hl-inlinetags">?&gt;</span><span class="hl-default">&lt;/li&gt;
            </span><span class="hl-inlinetags">&lt;?php</span><span class="hl-code"> </span><span class="hl-brackets">}</span><span class="hl-code"> </span><span class="hl-inlinetags">?&gt;</span>
<span class="hl-code">        &lt;/ul&gt;
    &lt;/body&gt;
</span>
</pre>
<h2>Adding Drag And Drop Functionality To Our List</h2>
<p>We will now add the drag/drop functionality to our list, as well as applying <span class="caps">CSS</span> styles to the list. At this point the ordering of the list will not be saved, as we will do this in the next step.</p>
<h3>Installing Scriptaculous</h3>
<p>Since we are using Scriptaculous to create the drag/drop effect, we must now download and install it. Note that we also need the Prototype library, however, this is included with the Scriptaculous download.</p>
<ul>
<li><a href="http://script.aculo.us/downloads">Scriptaculous download page</a></li>
</ul>
<p>This example uses Scriptaculous 1.5.3.</p>
<p>Once downloaded, extract the library in the directory where you saved index.php. You may save this elsewhere, but we will assume this is where you have saved it.</p>
<h3>Styling the list &ndash; styles.css</h3>
<p>Before we add the drag/drop, we will style the list. Below is a generic <span class="caps">CSS</span> class we will save to a file called styles.css.</p>
<div class="listing">
<div class="caption"><strong>Listing 9</strong> styles.css</div>
<div class="highlight">
<div class="hl-main">
<pre><span class="hl-identifier">.sortable-list</span><span class="hl-code"> </span><span class="hl-brackets">{</span>
<span class="hl-code">    </span><span class="hl-reserved">list-style-type :</span><span class="hl-code"> </span><span class="hl-string">none</span><span class="hl-code">;
    </span><span class="hl-reserved">margin :</span><span class="hl-code"> </span><span class="hl-number">0</span><span class="hl-code">;
</span><span class="hl-brackets">}</span>
<span class="hl-identifier">.sortable-list</span><span class="hl-code"> </span><span class="hl-identifier">li</span><span class="hl-code"> </span><span class="hl-brackets">{</span>
<span class="hl-code">    </span><span class="hl-reserved">border :</span><span class="hl-code"> </span><span class="hl-number">1</span><span class="hl-string">px</span><span class="hl-code"> </span><span class="hl-string">solid</span><span class="hl-code"> </span><span class="hl-var">#000</span><span class="hl-code">;
    </span><span class="hl-reserved">cursor :</span><span class="hl-code"> </span><span class="hl-string">move</span><span class="hl-code">;
    </span><span class="hl-reserved">margin :</span><span class="hl-code"> </span><span class="hl-number">2</span><span class="hl-string">px</span><span class="hl-code"> </span><span class="hl-number">0</span><span class="hl-code"> </span><span class="hl-number">2</span><span class="hl-string">px</span><span class="hl-code"> </span><span class="hl-number">0</span><span class="hl-code">;
    </span><span class="hl-reserved">padding :</span><span class="hl-code"> </span><span class="hl-number">3</span><span class="hl-string">px</span><span class="hl-code">;
    </span><span class="hl-reserved">background :</span><span class="hl-code"> </span><span class="hl-var">#f7f7f7</span><span class="hl-code">;
    </span><span class="hl-reserved">border :</span><span class="hl-code"> </span><span class="hl-var">#ccc</span><span class="hl-code">;
    </span><span class="hl-reserved">width :</span><span class="hl-code"> </span><span class="hl-number">400</span><span class="hl-string">px</span><span class="hl-code">;
</span><span class="hl-brackets">}</span></pre>
</div></div>
</p></div>
<h3>The Scriptaculous drag sort code</h3>
<p>It&rsquo;s really simple to make our list drag-sortable. At this point we&rsquo;re not actually saving the drag changes, but to make the list sortable, the following code is used:</p>
<div class="listing">
<div class="caption"><strong>Listing 10</strong> listing-10.js</div>
<div class="highlight">
<div class="hl-main">
<pre><span class="hl-identifier">Sortable</span><span class="hl-code">.</span><span class="hl-identifier">create</span><span class="hl-brackets">(</span><span class="hl-quotes">&#39;</span><span class="hl-string">movies_list</span><span class="hl-quotes">&#39;</span><span class="hl-brackets">)</span><span class="hl-code">;</span></pre>
</div></div>
</p></div>
<p>The name _movies_list_ refers to the ID of our unordered list.</p>
<p>There are many more options and effects that can be applied, but the default options work just fine for what we&rsquo;re doing. You can always read the <a href="http://wiki.script.aculo.us/">Scriptaculous documentation</a> for more options.</p>
<h3>Our new index.php</h3>
<p>So here is the new version of index.php, with styles added, Scriptaculous and Prototype loaded, and our draggable list created:</p>
<div class="listing">
<div class="caption"><strong>Listing 11</strong> index.php</div>
<div class="highlight">
<div class="hl-main">
<pre><span class="hl-inlinetags">&lt;?php</span>
<span class="hl-code">    </span><span class="hl-reserved">require_once</span><span class="hl-brackets">(</span><span class="hl-quotes">&#39;</span><span class="hl-string">database.php</span><span class="hl-quotes">&#39;</span><span class="hl-brackets">)</span><span class="hl-code">;
    </span><span class="hl-reserved">require_once</span><span class="hl-brackets">(</span><span class="hl-quotes">&#39;</span><span class="hl-string">movies.php</span><span class="hl-quotes">&#39;</span><span class="hl-brackets">)</span><span class="hl-code">;

    </span><span class="hl-reserved">if</span><span class="hl-code"> </span><span class="hl-brackets">(</span><span class="hl-code">!</span><span class="hl-identifier">dbConnect</span><span class="hl-brackets">())</span><span class="hl-code"> </span><span class="hl-brackets">{</span>
<span class="hl-code">        </span><span class="hl-reserved">echo</span><span class="hl-code"> </span><span class="hl-quotes">&#39;</span><span class="hl-string">Error connecting to database</span><span class="hl-quotes">&#39;</span><span class="hl-code">;
        </span><span class="hl-reserved">exit</span><span class="hl-code">;
    </span><span class="hl-brackets">}</span>
<span class="hl-code">
    </span><span class="hl-var">$movies</span><span class="hl-code"> = </span><span class="hl-identifier">getMovies</span><span class="hl-brackets">()</span><span class="hl-code">;
</span><span class="hl-inlinetags">?&gt;</span>
<span class="hl-code">&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Strict//EN&quot; &quot;DTD/xhtml1-strict.dtd&quot;&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;phpRiot Sortable Lists&lt;/title&gt;
        &lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;styles.css&quot; /&gt;

        &lt;script type=&quot;text/javascript&quot; src=&quot;scriptaculous-js-1.5.3/lib/prototype.js&quot;&gt;&lt;/script&gt;
        &lt;script type=&quot;text/javascript&quot; src=&quot;scriptaculous-js-1.5.3/src/scriptaculous.js&quot;&gt;&lt;/script&gt;
    &lt;/head&gt;
    &lt;body&gt;
        &lt;h1&gt;phpRiot Sortable Lists&lt;/h1&gt;

        &lt;ul id=&quot;movies_list&quot; class=&quot;sortable-list&quot;&gt;
            </span><span class="hl-inlinetags">&lt;?php</span><span class="hl-code"> </span><span class="hl-reserved">foreach</span><span class="hl-code"> </span><span class="hl-brackets">(</span><span class="hl-var">$movies</span><span class="hl-code"> </span><span class="hl-reserved">as</span><span class="hl-code"> </span><span class="hl-var">$movie_id</span><span class="hl-code"> =&gt; </span><span class="hl-var">$title</span><span class="hl-brackets">)</span><span class="hl-code"> </span><span class="hl-brackets">{</span><span class="hl-code"> </span><span class="hl-inlinetags">?&gt;</span>
<span class="hl-default">                &lt;li id=&quot;movie_</span><span class="hl-inlinetags">&lt;?=</span><span class="hl-code"> </span><span class="hl-var">$movie_id</span><span class="hl-code"> </span><span class="hl-inlinetags">?&gt;</span><span class="hl-default">&quot;&gt;</span><span class="hl-inlinetags">&lt;?=</span><span class="hl-code"> </span><span class="hl-var">$title</span><span class="hl-code"> </span><span class="hl-inlinetags">?&gt;</span><span class="hl-default">&lt;/li&gt;
            </span><span class="hl-inlinetags">&lt;?php</span><span class="hl-code"> </span><span class="hl-brackets">}</span><span class="hl-code"> </span><span class="hl-inlinetags">?&gt;</span>
<span class="hl-code">        &lt;/ul&gt;

        &lt;script type=&quot;text/javascript&quot;&gt;
            Sortable.create(&#39;movies_list&#39;);
        &lt;/script&gt;
    &lt;/body&gt;
</span>
</pre>
<h2>Creating The Order Processing Script</h2>
<p>Now we need to write the script that processes any ordering changes to the list. Once this is done, we&rsquo;ll add the functionality to our list to actually call this script.</p>
<p>When a change to the list occurs, an array of the movie ID&rsquo;s in their new order is generated, so our processor needs to take this array, and then update the ranking field in the database accordingly.</p>
<p>To achieve this, we create a new function in our movies.php, called <code>processMoviesOrder()</code>. Add this function after the <code>getMovies()</code> function in movies.php.</p>
<h3>processMoviesOrder() for MySQL</h3>
<div class="listing">
<div class="caption"><strong>Listing 12</strong> movies.php</div>
<div class="highlight">
<div class="hl-main">
<pre><span class="hl-inlinetags">&lt;?php</span>
<span class="hl-code">    </span><span class="hl-reserved">function</span><span class="hl-code"> </span><span class="hl-identifier">processMoviesOrder</span><span class="hl-brackets">(</span><span class="hl-var">$key</span><span class="hl-brackets">)</span>
<span class="hl-code">    </span><span class="hl-brackets">{</span>
<span class="hl-code">        </span><span class="hl-reserved">if</span><span class="hl-code"> </span><span class="hl-brackets">(</span><span class="hl-code">!</span><span class="hl-reserved">isset</span><span class="hl-brackets">(</span><span class="hl-var">$_POST</span><span class="hl-brackets">[</span><span class="hl-var">$key</span><span class="hl-brackets">])</span><span class="hl-code"> || !</span><span class="hl-identifier">is_array</span><span class="hl-brackets">(</span><span class="hl-var">$_POST</span><span class="hl-brackets">[</span><span class="hl-var">$key</span><span class="hl-brackets">]))</span>
<span class="hl-code">            </span><span class="hl-reserved">return</span><span class="hl-code">;

        </span><span class="hl-var">$movies</span><span class="hl-code"> = </span><span class="hl-identifier">getMovies</span><span class="hl-brackets">()</span><span class="hl-code">;
        </span><span class="hl-var">$queries</span><span class="hl-code"> = </span><span class="hl-reserved">array</span><span class="hl-brackets">()</span><span class="hl-code">;
        </span><span class="hl-var">$ranking</span><span class="hl-code"> = </span><span class="hl-number">1</span><span class="hl-code">;

        </span><span class="hl-reserved">foreach</span><span class="hl-code"> </span><span class="hl-brackets">(</span><span class="hl-var">$_POST</span><span class="hl-brackets">[</span><span class="hl-var">$key</span><span class="hl-brackets">]</span><span class="hl-code"> </span><span class="hl-reserved">as</span><span class="hl-code"> </span><span class="hl-var">$movie_id</span><span class="hl-brackets">)</span><span class="hl-code"> </span><span class="hl-brackets">{</span>
<span class="hl-code">            </span><span class="hl-reserved">if</span><span class="hl-code"> </span><span class="hl-brackets">(</span><span class="hl-code">!</span><span class="hl-identifier">array_key_exists</span><span class="hl-brackets">(</span><span class="hl-var">$movie_id</span><span class="hl-code">, </span><span class="hl-var">$movies</span><span class="hl-brackets">))</span>
<span class="hl-code">                </span><span class="hl-reserved">continue</span><span class="hl-code">;

            </span><span class="hl-var">$query</span><span class="hl-code"> = </span><span class="hl-identifier">sprintf</span><span class="hl-brackets">(</span><span class="hl-quotes">&#39;</span><span class="hl-string">update movies set ranking = %d where movie_id = %d</span><span class="hl-quotes">&#39;</span><span class="hl-code">,
                             </span><span class="hl-var">$ranking</span><span class="hl-code">,
                             </span><span class="hl-var">$movie_id</span><span class="hl-brackets">)</span><span class="hl-code">;

            </span><span class="hl-identifier">mysql_query</span><span class="hl-brackets">(</span><span class="hl-var">$query</span><span class="hl-brackets">)</span><span class="hl-code">;
            </span><span class="hl-var">$ranking</span><span class="hl-code">++;
        </span><span class="hl-brackets">}</span>
<span class="hl-code">    </span><span class="hl-brackets">}</span>
<span class="hl-inlinetags">?&gt;</span></pre>
</div></div>
</p></div>
<h3>processMoviesOrder() for PostgreSQL</h3>
<div class="listing">
<div class="caption"><strong>Listing 13</strong> listing-13.php</div>
<div class="highlight">
<div class="hl-main">
<pre><span class="hl-inlinetags">&lt;?php</span>
<span class="hl-code">    </span><span class="hl-reserved">function</span><span class="hl-code"> </span><span class="hl-identifier">processMoviesOrder</span><span class="hl-brackets">(</span><span class="hl-var">$key</span><span class="hl-brackets">)</span>
<span class="hl-code">    </span><span class="hl-brackets">{</span>
<span class="hl-code">        </span><span class="hl-reserved">if</span><span class="hl-code"> </span><span class="hl-brackets">(</span><span class="hl-code">!</span><span class="hl-reserved">isset</span><span class="hl-brackets">(</span><span class="hl-var">$_POST</span><span class="hl-brackets">[</span><span class="hl-var">$key</span><span class="hl-brackets">])</span><span class="hl-code"> || !</span><span class="hl-identifier">is_array</span><span class="hl-brackets">(</span><span class="hl-var">$_POST</span><span class="hl-brackets">[</span><span class="hl-var">$key</span><span class="hl-brackets">]))</span>
<span class="hl-code">            </span><span class="hl-reserved">return</span><span class="hl-code">;

        </span><span class="hl-var">$movies</span><span class="hl-code"> = </span><span class="hl-identifier">getMovies</span><span class="hl-brackets">()</span><span class="hl-code">;
        </span><span class="hl-var">$queries</span><span class="hl-code"> = </span><span class="hl-reserved">array</span><span class="hl-brackets">()</span><span class="hl-code">;
        </span><span class="hl-var">$ranking</span><span class="hl-code"> = </span><span class="hl-number">1</span><span class="hl-code">;

        </span><span class="hl-reserved">foreach</span><span class="hl-code"> </span><span class="hl-brackets">(</span><span class="hl-var">$_POST</span><span class="hl-brackets">[</span><span class="hl-var">$key</span><span class="hl-brackets">]</span><span class="hl-code"> </span><span class="hl-reserved">as</span><span class="hl-code"> </span><span class="hl-var">$movie_id</span><span class="hl-brackets">)</span><span class="hl-code"> </span><span class="hl-brackets">{</span>
<span class="hl-code">            </span><span class="hl-reserved">if</span><span class="hl-code"> </span><span class="hl-brackets">(</span><span class="hl-code">!</span><span class="hl-identifier">array_key_exists</span><span class="hl-brackets">(</span><span class="hl-var">$movie_id</span><span class="hl-code">, </span><span class="hl-var">$movies</span><span class="hl-brackets">))</span>
<span class="hl-code">                </span><span class="hl-reserved">continue</span><span class="hl-code">;

            </span><span class="hl-var">$query</span><span class="hl-code"> = </span><span class="hl-identifier">sprintf</span><span class="hl-brackets">(</span><span class="hl-quotes">&#39;</span><span class="hl-string">update movies set ranking = %d where movie_id = %d</span><span class="hl-quotes">&#39;</span><span class="hl-code">,
                             </span><span class="hl-var">$ranking</span><span class="hl-code">,
                             </span><span class="hl-var">$movie_id</span><span class="hl-brackets">)</span><span class="hl-code">;

            </span><span class="hl-identifier">pg_query</span><span class="hl-brackets">(</span><span class="hl-var">$query</span><span class="hl-brackets">)</span><span class="hl-code">;
            </span><span class="hl-var">$ranking</span><span class="hl-code">++;
        </span><span class="hl-brackets">}</span>
<span class="hl-code">    </span><span class="hl-brackets">}</span>
<span class="hl-inlinetags">?&gt;</span></pre>
</div></div>
</p></div>
<h3>processor.php for MySQL and PostgreSQL</h3>
<p>Now here is the script that calls the processMoviesOrder script. Note that we pass the form index that holds the ordering values. There&rsquo;s no great reason for doing this other than if you change the form key then you only have to change it here (note that this is the unordered list ID from <code>index.php</code>).</p>
<div class="listing">
<div class="caption"><strong>Listing 14</strong> listing-14.php</div>
<div class="highlight">
<div class="hl-main">
<pre><span class="hl-inlinetags">&lt;?php</span>
<span class="hl-code">    </span><span class="hl-reserved">require_once</span><span class="hl-brackets">(</span><span class="hl-quotes">&#39;</span><span class="hl-string">database.php</span><span class="hl-quotes">&#39;</span><span class="hl-brackets">)</span><span class="hl-code">;
    </span><span class="hl-reserved">require_once</span><span class="hl-brackets">(</span><span class="hl-quotes">&#39;</span><span class="hl-string">movies.php</span><span class="hl-quotes">&#39;</span><span class="hl-brackets">)</span><span class="hl-code">;

    </span><span class="hl-reserved">if</span><span class="hl-code"> </span><span class="hl-brackets">(</span><span class="hl-code">!</span><span class="hl-identifier">dbConnect</span><span class="hl-brackets">())</span>
<span class="hl-code">        </span><span class="hl-reserved">exit</span><span class="hl-code">;

</span>
</pre>
<h2>Adding The Javascript Sorting Callback</h2>
<p>The final item we must add is the JavaScript code to invoke <code>processor.php</code> when the list is updated. This involves creating a function that makes the Ajax update request, as well as telling the Scriptaculous <code>Sortable.create()</code> method about it.</p>
<p>Here&rsquo;s the callback function:</p>
<div class="listing">
<div class="caption"><strong>Listing 15</strong> listing-15.js</div>
<div class="highlight">
<div class="hl-main">
<pre><span class="hl-reserved">function</span><span class="hl-code"> </span><span class="hl-identifier">updateOrder</span><span class="hl-brackets">()</span>
<span class="hl-brackets">{</span>
<span class="hl-code">    </span><span class="hl-reserved">var</span><span class="hl-code"> </span><span class="hl-identifier">options</span><span class="hl-code"> = </span><span class="hl-brackets">{</span>
<span class="hl-code">                    </span><span class="hl-identifier">method</span><span class="hl-code"> : </span><span class="hl-quotes">&#39;</span><span class="hl-string">post</span><span class="hl-quotes">&#39;</span><span class="hl-code">,
                    </span><span class="hl-identifier">parameters</span><span class="hl-code"> : </span><span class="hl-identifier">Sortable</span><span class="hl-code">.</span><span class="hl-identifier">serialize</span><span class="hl-brackets">(</span><span class="hl-quotes">&#39;</span><span class="hl-string">movies_list</span><span class="hl-quotes">&#39;</span><span class="hl-brackets">)</span>
<span class="hl-code">                  </span><span class="hl-brackets">}</span><span class="hl-code">;

    </span><span class="hl-reserved">new</span><span class="hl-code"> </span><span class="hl-identifier">Ajax</span><span class="hl-code">.</span><span class="hl-identifier">Request</span><span class="hl-brackets">(</span><span class="hl-quotes">&#39;</span><span class="hl-string">processor.php</span><span class="hl-quotes">&#39;</span><span class="hl-code">, </span><span class="hl-identifier">options</span><span class="hl-brackets">)</span><span class="hl-code">;
</span><span class="hl-brackets">}</span></pre>
</div></div>
</p></div>
<p>Here we invoke the Prototype library&rsquo;s Ajax request handler to call <code>processor.php</code>. Additionally, we use the <code>serialize()</code> method on the Scriptaculous Sortable object to create the <span class="caps">POST</span> variable we access in <code>processor.php</code>.</p>
<p>Finally, we modify our list creation to tell it about this <code>updateOrder()</code> callback:</p>
<div class="listing">
<div class="caption"><strong>Listing 16</strong> listing-16.js</div>
<div class="highlight">
<div class="hl-main">
<pre><span class="hl-identifier">Sortable</span><span class="hl-code">.</span><span class="hl-identifier">create</span><span class="hl-brackets">(</span><span class="hl-quotes">&#39;</span><span class="hl-string">movies_list</span><span class="hl-quotes">&#39;</span><span class="hl-code">, </span><span class="hl-brackets">{</span><span class="hl-code"> </span><span class="hl-identifier">onUpdate</span><span class="hl-code"> : </span><span class="hl-identifier">updateOrder</span><span class="hl-code"> </span><span class="hl-brackets">})</span><span class="hl-code">;</span></pre>
</div></div>
</p></div>
<p>The second parameter to <code>Sortable.create()</code> is an optional list of extra parameters. In this case we are just specifying the <code>onUpdate</code> parameter, which tells Sortable which function to call when the list is changed.</p>
<h3>index.php for MySQL and PostgreSQL in full</h3>
<div class="listing">
<div class="caption"><strong>Listing 17</strong> listing-17.php</div>
<div class="highlight">
<div class="hl-main">
<pre><span class="hl-inlinetags">&lt;?php</span>
<span class="hl-code">    </span><span class="hl-reserved">require_once</span><span class="hl-brackets">(</span><span class="hl-quotes">&#39;</span><span class="hl-string">database.php</span><span class="hl-quotes">&#39;</span><span class="hl-brackets">)</span><span class="hl-code">;
    </span><span class="hl-reserved">require_once</span><span class="hl-brackets">(</span><span class="hl-quotes">&#39;</span><span class="hl-string">movies.php</span><span class="hl-quotes">&#39;</span><span class="hl-brackets">)</span><span class="hl-code">;

    </span><span class="hl-reserved">if</span><span class="hl-code"> </span><span class="hl-brackets">(</span><span class="hl-code">!</span><span class="hl-identifier">dbConnect</span><span class="hl-brackets">())</span><span class="hl-code"> </span><span class="hl-brackets">{</span>
<span class="hl-code">        </span><span class="hl-reserved">echo</span><span class="hl-code"> </span><span class="hl-quotes">&#39;</span><span class="hl-string">Error connecting to database</span><span class="hl-quotes">&#39;</span><span class="hl-code">;
        </span><span class="hl-reserved">exit</span><span class="hl-code">;
    </span><span class="hl-brackets">}</span>
<span class="hl-code">
    </span><span class="hl-var">$movies</span><span class="hl-code"> = </span><span class="hl-identifier">getMovies</span><span class="hl-brackets">()</span><span class="hl-code">;
</span><span class="hl-inlinetags">?&gt;</span>
<span class="hl-code">&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Strict//EN&quot; &quot;DTD/xhtml1-strict.dtd&quot;&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;phpRiot Sortable Lists&lt;/title&gt;
        &lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;styles.css&quot; /&gt;

        &lt;script type=&quot;text/javascript&quot; src=&quot;scriptaculous-js-1.5.3/lib/prototype.js&quot;&gt;&lt;/script&gt;
        &lt;script type=&quot;text/javascript&quot; src=&quot;scriptaculous-js-1.5.3/src/scriptaculous.js&quot;&gt;&lt;/script&gt;
    &lt;/head&gt;
    &lt;body&gt;
        &lt;h1&gt;phpRiot Sortable Lists&lt;/h1&gt;

        &lt;ul id=&quot;movies_list&quot; class=&quot;sortable-list&quot;&gt;
            </span><span class="hl-inlinetags">&lt;?php</span><span class="hl-code"> </span><span class="hl-reserved">foreach</span><span class="hl-code"> </span><span class="hl-brackets">(</span><span class="hl-var">$movies</span><span class="hl-code"> </span><span class="hl-reserved">as</span><span class="hl-code"> </span><span class="hl-var">$movie_id</span><span class="hl-code"> =&gt; </span><span class="hl-var">$title</span><span class="hl-brackets">)</span><span class="hl-code"> </span><span class="hl-brackets">{</span><span class="hl-code"> </span><span class="hl-inlinetags">?&gt;</span>
<span class="hl-default">                &lt;li id=&quot;movie_</span><span class="hl-inlinetags">&lt;?=</span><span class="hl-code"> </span><span class="hl-var">$movie_id</span><span class="hl-code"> </span><span class="hl-inlinetags">?&gt;</span><span class="hl-default">&quot;&gt;</span><span class="hl-inlinetags">&lt;?=</span><span class="hl-code"> </span><span class="hl-var">$title</span><span class="hl-code"> </span><span class="hl-inlinetags">?&gt;</span><span class="hl-default">&lt;/li&gt;
            </span><span class="hl-inlinetags">&lt;?php</span><span class="hl-code"> </span><span class="hl-brackets">}</span><span class="hl-code"> </span><span class="hl-inlinetags">?&gt;</span>
<span class="hl-code">        &lt;/ul&gt;

        &lt;script type=&quot;text/javascript&quot;&gt;
            function updateOrder()
            {
                var options = {
                                method : &#39;post&#39;,
                                parameters : Sortable.serialize(&#39;movies_list&#39;)
                              };

                new Ajax.Request(&#39;processor.php&#39;, options);
            }

            Sortable.create(&#39;movies_list&#39;, { onUpdate : updateOrder });
        &lt;/script&gt;
    &lt;/body&gt;
</span>
</pre>
<h2>Summary</h2>
<p>In this article we learned how to create a sortable list using <span class="caps">PHP</span> and Ajax. We used Scriptaculous and Prototype libraries to make light work of our JavaScript requirements (the sorting and Ajax requests), as these libraries provide a very powerful and simple interface to advanced features and effects.</p>
<h3>Error handling</h3>
<p>We didn&rsquo;t deal with error handling at all in this article, for the sake of simplicity. Specifically, we didn&rsquo;t specify what would happen if the update didn&rsquo;t work. If the update failed, the list would appear to be updated, but when you refreshed the list it would be the old state.</p>
<p>One possible way to handle this would be to send a success/failure indication from processor.php, and then to read this response in index.php, rolling back the drag and drop if failure was returned.</p>
<h3>Extra features</h3>
<p>When you update the list, the saving of the new ordering is a very quick process, but it is possible that sometimes it could take longer due to latency or server load. As such, you might think about showing then hiding a message while performing the update.</p>
<p>To do this, you would make the message appear when <code>updateOrder()</code> is called, and then create another function to hide the message once complete. This is achieved by specifying the <code>onComplete</code> parameter in the options array for the Ajax request.</p>
<p>Here&rsquo;s an example:</p>
<div class="listing">
<div class="caption"><strong>Listing 18</strong> listing-18.js</div>
<div class="highlight">
<div class="hl-main">
<pre><span class="hl-reserved">function</span><span class="hl-code"> </span><span class="hl-identifier">updateOrder</span><span class="hl-brackets">()</span>
<span class="hl-brackets">{</span>
<span class="hl-code">    </span><span class="hl-comment">// turn on update message here</span>
<span class="hl-code">
    </span><span class="hl-reserved">var</span><span class="hl-code"> </span><span class="hl-identifier">options</span><span class="hl-code"> = </span><span class="hl-brackets">{</span>
<span class="hl-code">                    </span><span class="hl-identifier">method</span><span class="hl-code"> : </span><span class="hl-quotes">&#39;</span><span class="hl-string">post</span><span class="hl-quotes">&#39;</span><span class="hl-code">,
                    </span><span class="hl-identifier">parameters</span><span class="hl-code"> : </span><span class="hl-identifier">Sortable</span><span class="hl-code">.</span><span class="hl-identifier">serialize</span><span class="hl-brackets">(</span><span class="hl-quotes">&#39;</span><span class="hl-string">movies_list</span><span class="hl-quotes">&#39;</span><span class="hl-brackets">)</span><span class="hl-code">,
                    </span><span class="hl-identifier">onComplete</span><span class="hl-code"> : </span><span class="hl-reserved">function</span><span class="hl-brackets">(</span><span class="hl-identifier">request</span><span class="hl-brackets">)</span><span class="hl-code"> </span><span class="hl-brackets">{</span>
<span class="hl-code">                        </span><span class="hl-comment">// turn off update message here</span>
<span class="hl-code">                    </span><span class="hl-brackets">}</span>
<span class="hl-code">                  </span><span class="hl-brackets">}</span><span class="hl-code">;
</span>
<span class="hl-code">    </span><span class="hl-reserved">new</span><span class="hl-code"> </span><span class="hl-identifier">Ajax</span><span class="hl-code">.</span><span class="hl-identifier">Request</span><span class="hl-brackets">(</span><span class="hl-quotes">&#39;</span><span class="hl-string">processor.php</span><span class="hl-quotes">&#39;</span><span class="hl-code">, </span><span class="hl-identifier">options</span><span class="hl-brackets">)</span><span class="hl-code">;
</span><span class="hl-brackets">}</span></pre>
</div></div>
</p></div>
<p>I&rsquo;ll leave this as an exercise for you to complete. Hint: create a div which you initially set the <span class="caps">CSS</span> <code>display</code> property to <code>none</code>. Then set it to <code>block</code> to show the div, and set it back to <code>none</code> to hide it again.</p>
</p></div>
</p></div>
</p></div>
</p></div>
</p></div>
</p></div>
</p></div>
</p></div>
</p></div>
<p>			<strong>Quentin Zervaas</strong></div>
</p></div>
</div>
]]></content:encoded>
			<wfw:commentRss>http://www.allfreetech.com/php/creating-sortable-lists-with-php-and-ajax-42.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Creating a Multi-File Upload Script in PHP</title>
		<link>http://www.allfreetech.com/php/creating-a-multi-file-upload-script-in-php-17.html</link>
		<comments>http://www.allfreetech.com/php/creating-a-multi-file-upload-script-in-php-17.html#comments</comments>
		<pubDate>Fri, 22 Jan 2010 12:23:51 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[file upload]]></category>

		<guid isPermaLink="false">http://www.allfreetech.com/?p=17</guid>
		<description><![CDATA[Frustrated with single-file upload scripts? Looking for an alternate route? Read as Jonathan shows us how easy it really is to setup a multi-file upload script using PHP. &#160; As a PHP programmer I had run into a problem where a client needed a form to upload more than one file at a time. So [...]]]></description>
			<content:encoded><![CDATA[<p><span class="Apple-style-span" style="color: rgb(0, 0, 0); font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 11px; ">Frustrated with single-file upload scripts? Looking for an alternate route? Read as Jonathan shows us how easy it really is to setup a multi-file upload script using PHP.<span id="more-17"></span></span></p>
<p>&nbsp;</p>
<p><span class="Apple-style-span" style="color: rgb(0, 0, 0); font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 11px; "><span>As a PHP programmer I had run into a problem where a client needed a form to upload more than one file at a time. So one night I sat down and spent an hour figuring out the best and easiest way to do this. In this tutorial, the&nbsp;<b>for loop</b>&nbsp;is going to be your best friend.</span></span></p>
<div>
<h4><span class="Apple-style-span" style="color: rgb(0, 0, 0); font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 11px; "><font face="Arial">uploadForm1.php</font></span></h4>
<p><span class="Apple-style-span" style="color: rgb(0, 0, 0); font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 11px; "><font face="Courier New" size="2" style="background-color: rgb(255, 255, 0); ">&lt;html&gt;<br />
		&lt;head&gt;<br />
		&lt;title&gt;# of Files to Upload&lt;/title&gt;<br />
		&lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=iso-8859-1&quot;&gt;<br />
		&lt;/head&gt;</font></span></p>
<p><span class="Apple-style-span" style="color: rgb(0, 0, 0); font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 11px; "><font face="Courier New" size="2" style="background-color: rgb(255, 255, 0); ">&lt;body&gt;<br />
		&lt;form name=&quot;form1&quot; method=&quot;post&quot; action=&quot;uploadForm2.php&quot;&gt;<br />
		&nbsp; &lt;p&gt;Enter the amount of boxes you will need below. Max = 9.&lt;/p&gt;<br />
		&nbsp; &lt;p&gt;<br />
		&nbsp;&nbsp;&nbsp; &lt;input name=&quot;uploadNeed&quot; type=&quot;text&quot; id=&quot;uploadNeed&quot; maxlength=&quot;1&quot;&gt;<br />
		&nbsp; &lt;/p&gt;<br />
		&nbsp; &lt;p&gt;<br />
		&nbsp;&nbsp;&nbsp; &lt;input type=&quot;submit&quot; name=&quot;Submit&quot; value=&quot;Submit&quot;&gt;<br />
		&nbsp; &lt;/p&gt;<br />
		&lt;/form&gt;<br />
		&lt;/body&gt;<br />
		&lt;/html&gt;</font></span></p>
<p><span class="Apple-style-span" style="color: rgb(0, 0, 0); font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 11px; "><span>As you can see this first page is very basic. In my form I set the uploadNeed maxlength to 1. This way the max upload boxes he or she can get is 9. You can increase or decrease this to satisfy your own project needs.</span></span></p>
<p>&nbsp;</p>
<h4><span><font face="Arial">uploadForm2.php</font></span></h4>
<p>Ok, this page will be doing one half of the work. We will be using the for loop to get this task done.</p>
<p><font face="Courier New" size="2" style="background-color: rgb(255, 255, 0); ">&lt;html&gt;<br />
		&lt;head&gt;<br />
		&lt;title&gt;Untitled Document&lt;/title&gt;<br />
		&lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=iso-8859-1&quot;&gt;<br />
		&lt;/head&gt;</font></p>
<p><font face="Courier New" size="2" style="background-color: rgb(255, 255, 0); ">&lt;body&gt;</font></p>
<p><font face="Courier New" size="2" style="background-color: rgb(255, 255, 0); ">&lt;form name=&quot;form1&quot; enctype=&quot;multipart/form-data&quot; method=&quot;post&quot; action=&quot;processFiles.php&quot;&gt;<br />
		&nbsp; &lt;p&gt;<br />
		&nbsp; &lt;?<br />
		&nbsp; // start of dynamic form<br />
		&nbsp; $uploadNeed = $_POST[&#39;uploadNeed&#39;];<br />
		&nbsp; for($x=0;$x&lt;$uploadNeed;$x++){<br />
		&nbsp; ?&gt;<br />
		&nbsp;&nbsp;&nbsp; &lt;input name=&quot;uploadFile&lt;? echo $x;?&gt;&quot; type=&quot;file&quot; id=&quot;uploadFile&lt;? echo $x;?&gt;&quot;&gt;<br />
		&nbsp; &lt;/p&gt;<br />
		&nbsp; &lt;?<br />
		&nbsp; // end of for loop<br />
		&nbsp; }<br />
		&nbsp; ?&gt;<br />
		&nbsp; &lt;p&gt;&lt;input name=&quot;uploadNeed&quot; type=&quot;hidden&quot; value=&quot;&lt;? echo $uploadNeed;?&gt;&quot;&gt;<br />
		&nbsp;&nbsp;&nbsp; &lt;input type=&quot;submit&quot; name=&quot;Submit&quot; value=&quot;Submit&quot;&gt;<br />
		&nbsp; &lt;/p&gt;<br />
		&lt;/form&gt;<br />
		&lt;/body&gt;<br />
		&lt;/html&gt;</font></p>
<p>In this page, all I did was create a simple HTML form with the value of the attribute &quot;type&quot; set to &quot;file&quot;. Within the form I put a block of code to start the for loop. I set $x to 0 and I made it stop at the desired need by setting $x to be less than $uploadNeed &ndash; the value specified by the user. I also echo the $uploadNeed into a hidden input field to be carried over to the last page.</p>
<p>The key to making this all work however is the $x variable I am echoing right next to the uploadFile name. What this will do is append a number starting with 0 to the name. This in turn will make each upload field&rsquo;s name unique.</p>
<p>&nbsp;</p>
<h4><span><font face="Arial">processFiles.php</font></span></h4>
<p>Here is the last page to complete our multiple upload tasks.</p>
<p><span><font face="Courier New" size="2" style="background-color: rgb(255, 255, 0); ">&lt;?<br />
		$uploadNeed = $_POST[&#39;uploadNeed&#39;];<br />
		// start for loop<br />
		for($x=0;$x&lt;$uploadNeed;$x++){<br />
		$file_name = $_FILES[&#39;uploadFile&#39;. $x][&#39;name&#39;];<br />
		// strip file_name of slashes<br />
		$file_name = stripslashes($file_name);<br />
		$file_name = str_replace(&quot;&#39;&quot;,&quot;&quot;,$file_name);<br />
		$copy = copy($_FILES[&#39;uploadFile&#39;. $x][&#39;tmp_name&#39;],$file_name);<br />
		&nbsp;// check if successfully copied<br />
		&nbsp;if($copy){<br />
		&nbsp;echo &quot;$file_name | uploaded sucessfully!&lt;br&gt;&quot;;<br />
		&nbsp;}else{<br />
		&nbsp;echo &quot;$file_name | could not be uploaded!&lt;br&gt;&quot;;<br />
		&nbsp;}<br />
		} // end of loop<br />
		?&gt;</font></span></p>
<p><span><span>The first thing we do in this page is grab the uploadNeed from uploadForm2.php. We setup our for loop in the same fashion as the last page. The difference here though is we get the $_FILES name within the for loop. I assign this to the local variable name $file_name.</span></span></p>
<p>&nbsp;</p>
<p><span><span>Next, we do a little parsing by adding the stripslashes and str_replace functions. The reason we add the stripslashes is due to file that may have apostrophes in their name; otherwise this will generate a parse error and prevent that file from being uploaded.</span></span></p>
<p>&nbsp;</p>
<p><span><span>Notice once again how I add the $x variable, which in turn is a number, to the name of the $_FILES. By doing this the script now knows which file it is uploading.</span></span></p>
<p>&nbsp;</p>
<p><span><span>We will use the copy function now to actually begin the upload process. The last thing I added was a simple if statement to check that the copy was successful and I echo that message out to the screen.</span></span></p>
<p>&nbsp;</p>
<p><span>This little script I am sure will come in handy when you have multiple files that you want to upload all at once. Some other things you could add to this script are listed below.</span></p>
<ul>
<li><span>Insert file names into a mysql database.</span></li>
<li><span>Get the size of the file and store that in the database too.</span></li>
<li><span>Create a temp text file delimited by commas for the files that have trouble uploading and at the end of the script do a retry on those files.</span></li>
<li><span>Create a mass-delete of files.</span></li>
</ul>
<p>Jonathan Wichmann</p>
</div>
]]></content:encoded>
			<wfw:commentRss>http://www.allfreetech.com/php/creating-a-multi-file-upload-script-in-php-17.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
