<?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>Xavisys&#187; PHP</title>
	<atom:link href="http://xavisys.com/category/php/feed/" rel="self" type="application/rss+xml" />
	<link>http://xavisys.com</link>
	<description>WordPress Plugins and Custom WordPress Development</description>
	<lastBuildDate>Wed, 16 Nov 2011 20:45:51 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
	<atom:link rel='hub' href='http://xavisys.com/?pushpress=hub'/>
		<item>
		<title>Howto: Create and Stream a CSV with PHP</title>
		<link>http://xavisys.com/howto-create-stream-csv-php/</link>
		<comments>http://xavisys.com/howto-create-stream-csv-php/#comments</comments>
		<pubDate>Tue, 27 Oct 2009 06:10:22 +0000</pubDate>
		<dc:creator>Aaron D. Campbell</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[File Download]]></category>
		<category><![CDATA[Stream Files]]></category>

		<guid isPermaLink="false">http://xavisys.com/?p=500</guid>
		<description><![CDATA[I find myself constantly creating csv files for users to download. Whether it&#8217;s a method for users to export their data to excel, a way for users to backup their hosted data, or just a simple way to send them report information, csv files are extremely useful. Normally I just create an actual file and [...]]]></description>
			<content:encoded><![CDATA[<p>I find myself constantly creating <abbr title="Comma Separated Values">csv</abbr> files for users to download.  Whether it&#8217;s a method for users to export their data to excel, a way for users to backup their hosted data, or just a simple way to send them report information, <abbr title="Comma Separated Values">csv</abbr> files are extremely useful.   Normally I just create an actual file and link to it for the user to download.  The files are usually cleaned up after a certain amount of time or after a certain number of newer files exist.</p>
<p>Recently however, I had a client that really wanted to be able to export data in <abbr title="Comma Separated Values">csv</abbr> format without ever creating a file on the webserver.  Their concern was rooted in security, but the reality of the matter was that they were trying to obey the letter of the law with regards to company policies.  Whether it was truly necessary or not is for another discussion.  Instead, the technique is actually very useful so I thought I&#8217;d share.</p>
<p>The key to this is that the <a href="http://php.net/fopen">fopen</a> function supports <a href="http://php.net/wrappers.php">php input/output streams</a> as <a href="http://us2.php.net/wrappers">wrappers</a>.</p>
<p><span id="more-500"></span></p>
<p>Start by sending the headers to allow the user to download the <abbr title="Comma Separated Values">csv</abbr> file, then open a stream:</p>
<pre class="brush: php; title: ; notranslate">
$fileName = 'somefile.csv';

header(&quot;Cache-Control: must-revalidate, post-check=0, pre-check=0&quot;);
header('Content-Description: File Transfer');
header(&quot;Content-type: text/csv&quot;);
header(&quot;Content-Disposition: attachment; filename={$fileName}&quot;);
header(&quot;Expires: 0&quot;);
header(&quot;Pragma: public&quot;);

$fh = @fopen( 'php://output', 'w' );
</pre>
<p>At this point, anything you send to the newly opened stream ends up in the downloaded <abbr title="Comma Separated Values">csv</abbr> file.  I did this in WordPress, but you can pull your data from anywhere, just pass an array to <a href="http://php.net/fputcsv">fputcsv()</a>.  In this case, $data is an associative array with the field name as the key and the field value as the value.</p>
<pre class="brush: php; title: ; notranslate">
global $wpdb;
$query = &quot;SELECT * FROM `{$wpdb-&gt;prefix}my_table`&quot;;
$results = $wpdb-&gt;get_results( $query, ARRAY_A );

$headerDisplayed = false;

foreach ( $results as $data ) {
	// Add a header row if it hasn't been added yet
	if ( !$headerDisplayed ) {
		// Use the keys from $data as the titles
		fputcsv($fh, array_keys($data));
		$headerDisplayed = true;
	}

	// Put the data into the stream
	fputcsv($fh, $data);
}
// Close the file
fclose($fh);
// Make sure nothing else is sent, our file is done
exit;
</pre>
<p>As you can see, the actual code is quite simple.  Here it is all put together:</p>
<pre class="brush: php; title: ; notranslate">
$fileName = 'somefile.csv';

header(&quot;Cache-Control: must-revalidate, post-check=0, pre-check=0&quot;);
header('Content-Description: File Transfer');
header(&quot;Content-type: text/csv&quot;);
header(&quot;Content-Disposition: attachment; filename={$fileName}&quot;);
header(&quot;Expires: 0&quot;);
header(&quot;Pragma: public&quot;);

$fh = @fopen( 'php://output', 'w' );

global $wpdb;
$query = &quot;SELECT * FROM `{$wpdb-&gt;prefix}my_table`&quot;;
$results = $wpdb-&gt;get_results( $query, ARRAY_A );

$headerDisplayed = false;

foreach ( $results as $data ) {
	// Add a header row if it hasn't been added yet
	if ( !$headerDisplayed ) {
		// Use the keys from $data as the titles
		fputcsv($fh, array_keys($data));
		$headerDisplayed = true;
	}

	// Put the data into the stream
	fputcsv($fh, $data);
}
// Close the file
fclose($fh);
// Make sure nothing else is sent, our file is done
exit;
</pre>
<p>I hope you find it useful!<br />
<h3 class='related_post_title'>Related Posts:</h3>
<ul class='related_post'>
<li><a href='http://xavisys.com/wordpress-25-shortcodes/' title='WordPress 2.5 Shortcodes'>WordPress 2.5 Shortcodes</a></li>
<li><a href='http://xavisys.com/wordpress-should-update-get_sidebar-to-work-with-multiple-sidebars/' title='Wordpress Should Update get_sidebar to Work With Multiple Sidebars'>WordPress Should Update get_sidebar to Work With Multiple Sidebars</a></li>
<li><a href='http://xavisys.com/php-function-to-redirect-a-user-with-a-message/' title='PHP function to Redirect a user with a message'>PHP function to Redirect a user with a message</a></li>
<li><a href='http://xavisys.com/replace-every-other-occurrence-with-str_replace/' title='Replace every other occurrence with str_replace'>Replace every other occurrence with str_replace</a></li>
<li><a href='http://xavisys.com/php-pagination-with-mysqli/' title='PHP Pagination with mysqli'>PHP Pagination with mysqli</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://xavisys.com/howto-create-stream-csv-php/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>Can open source eCommerce contend?</title>
		<link>http://xavisys.com/can-open-source-ecommerce-contend/</link>
		<comments>http://xavisys.com/can-open-source-ecommerce-contend/#comments</comments>
		<pubDate>Sun, 06 Jan 2008 06:48:02 +0000</pubDate>
		<dc:creator>Aaron D. Campbell</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Web Design]]></category>
		<category><![CDATA[CubeCart]]></category>
		<category><![CDATA[eCommerce]]></category>
		<category><![CDATA[magento]]></category>
		<category><![CDATA[Open Source]]></category>
		<category><![CDATA[oscommerce]]></category>
		<category><![CDATA[zencart]]></category>
		<category><![CDATA[zend framework]]></category>

		<guid isPermaLink="false">http://xavisys.com/can-open-source-ecommerce-contend/</guid>
		<description><![CDATA[It seems that sometimes work comes in waves. I haven&#8217;t dealt with setting up a shopping cart on a site in quite a while, and now I have three clients that I&#8217;m setting up shopping carts for. The clients want a PHP based solution (good thing, considering that&#8217;s what I do), and as usual the [...]]]></description>
			<content:encoded><![CDATA[<p>It seems that sometimes work comes in waves.  I haven&#8217;t dealt with setting up a shopping cart on a site in quite a while, and now I have three clients that I&#8217;m setting up shopping carts for. The clients want a PHP based solution (good thing, considering that&#8217;s what I do), and as usual the less we spend the better.  I started by looking at the available <acronym title="Free and open source software">FOSS</acronym> options, fully expecting that this would be a simple task.  Little did I know&#8230;</p>
<p>The first application I looked into was <a href="http://oscommerce.com/" title="Open Source eCommerce Solution">osCommerce</a>. The best way I can describe it is as an old dragon.  It may be free and open source, but it&#8217;s big, bulky, and outdated. I was looking for something much easier to use, and much more current. Something that would be easy to manage once it was set up, as opposed to taking two hours to add color options to a product.</p>
<p>Not to be discouraged, I moved on another possibility, <a href="http://zencart.com/" title="The Art of eCommerce">zenCart</a>. ZenCart is a huge step in the right direction, but it still seemed to lack the intuitive interface that you might expect from web based software.  I may be too hard on them, but a quality user interface makes the difference between happy customers, and customers that never return.  All in all, there is a lot of really great technology which zenCart doesn&#8217;t use, and while it&#8217;s free and open source, that doesn&#8217;t make up for it&#8217;s lack of usability.</p>
<p>I continued to look around at free alternatives, but didn&#8217;t find anything noteworthy.  Now I was discouraged. I decided to check into some commercial products, most notably <a href="http://cubecart.com/site/home/" title="eCommerce Modified">cubeCart</a>. It costs $130 &#8211; $180 and it&#8217;s not 100% open source, but cubeCart makes up for all that with the interface.  It has an intuitive admin section, better support, and plenty of available add-on modules (for shipping, payment, even affiliate programs).  In the end, we went with cubeCart, deciding that the benefits were worth the cost.  I was almost ready to admit that the available <acronym title="Free and open source software">FOSS</acronym> options couldn&#8217;t touch commercial products in this market.  Just then, a glimmer of hope!  <a href="http://www.magentocommerce.com/" title="Open source eCommerce Evolved">Magento</a>.</p>
<p><a href="http://www.magentocommerce.com/" title="Open source eCommerce Evolved">Magento</a> is a new up and coming PHP shopping cart, built using the <a href="http://framework.zend.com/" title="Open-Source PHP Framework">Zend Framework</a>.  It is young and currently still in beta, but it shows great promise.  According to their <a href="http://www.magentocommerce.com/roadmap" title="Magento Roadmap">roadmap</a>, the production version is due out first quarter 2008.  It&#8217;s current drawbacks are it&#8217;s lack of support for certain payment and shipping gateways, and it&#8217;s lack of support for popular affiliate programs.  However, much of this is in the roadmap, and should make it into the production version.  In my experience, what they currently have out is stable, and extremely user friendly.  I can finally breathe a sigh of relief, knowing that there will soon be a <acronym title="Free and open source software">FOSS</acronym> option that will be able to compete with their commercial counterparts.</p>
<p>In the end, if you need something right now, cubeCart is for you.  While it will require some up front investment, you will save it back just on the Tylenol you won&#8217;t be buying for the headaches you will have with osCommerce or zenCart.  However, if you don&#8217;t need something for a few months, or you are trying to keep an eye to the future, check out magento.  You&#8217;ll be glad you did.<br />
<h3 class='related_post_title'>Related Posts:</h3>
<ul class='related_post'>
<li><a href='http://xavisys.com/more-on-magento/' title='More on Magento'>More on Magento</a></li>
<li><a href='http://xavisys.com/how-to-profit-with-the-open-source-community/' title='How to Profit with the Open Source Community'>How to Profit with the Open Source Community</a></li>
<li><a href='http://xavisys.com/whats-the-best-cms/' title='What&#039;s the best CMS?'>What&#039;s the best CMS?</a></li>
<li><a href='http://xavisys.com/reorder-gallery-now-in-wordpress-core/' title='Reorder Gallery now in WordPress Core'>Reorder Gallery now in WordPress Core</a></li>
<li><a href='http://xavisys.com/why-contribute-to-the-open-source-community/' title='Why Contribute to the Open Source Community'>Why Contribute to the Open Source Community</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://xavisys.com/can-open-source-ecommerce-contend/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>PHP function to Redirect a user with a message</title>
		<link>http://xavisys.com/php-function-to-redirect-a-user-with-a-message/</link>
		<comments>http://xavisys.com/php-function-to-redirect-a-user-with-a-message/#comments</comments>
		<pubDate>Tue, 10 Jul 2007 03:51:19 +0000</pubDate>
		<dc:creator>Aaron D. Campbell</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[redirect]]></category>

		<guid isPermaLink="false">http://xavisys.com/php-function-to-redirect-a-user-with-a-message/</guid>
		<description><![CDATA[One of the common things that you need to do in PHP is redirect a user, and display a message on whatever page you are sending them to. There are a lot of reasons for this, but the most common is to avoid a user refresh from resubmitting a form. Instead, process the form data, [...]]]></description>
			<content:encoded><![CDATA[<p>One of the common things that you need to do in PHP is redirect a user, and display a message on whatever page you are sending them to.  There are a lot of reasons for this, but the most common is to avoid a user refresh from resubmitting a form.  Instead, process the form data, and redirect the user (even if you redirect them to the same page).  Here is a function I use for this very purpose.  It requires that you are using sessions.  It stores the message in the session, and passes a has in the URL.  This way, you can use any length message you want, and display it only once.<br />
<span id="more-46"></span><br />
First, the function:</p>
<pre class="brush: php; title: ; notranslate">/**
* This function redirects to a specified page (current page by default), but
* passes along a message in the users SESSION.  It passes a GET var that tells
* where that message is.  Used properly, this will avoid giving the user a
* duplicate message on page refresh.
*
* @param string $message - Message to pass along via session
* @param string[optional] $page - page to redirect to (with leading /)
*/
function redirect($message, $page=FALSE) {
	$my_get = array();
	$_GET['message'] = set_session_message($message);
	foreach ($_GET as $n=&gt;$v) {
		$my_get[] = &quot;{$n}={$v}&quot;;
	}
	if (count($my_get) &gt; 0) {
		$my_get = '?'.implode('&amp;',$my_get);
	} else {
		$my_get = '';
	}

	if (is_string($page)) {
		$location = $page;
	} else {
		$location = $_SERVER['SCRIPT_NAME'];
	}

	$http = (!isset($_SERVER['HTTPS']) || strtolower($_SERVER['HTTPS'])!='on')?'http':'https';

	header(&quot;Location: {$http}://{$_SERVER['HTTP_HOST']}{$location}{$my_get}&quot;);
	exit;
}

/**
 * Set a session message
 *
 * @param string $message Message to set
 *
 * @return string - Message ID which is the $_SESSION index that holds the message
 */
function set_session_message($message) {
	$message_id = sha1(microtime(true));
	$_SESSION[$message_id] = $message;

	return $message_id;
}
</pre>
<p>Use it like this:</p>
<pre class="brush: php; title: ; notranslate">
//To redirect to the current page
redirect('Place your message here');
//To go to another page
redirect('Place your message here', '/next_page.php');
</pre>
<p>Now, you will want to display the message.  I use this code in my template file</p>
<pre class="brush: php; title: ; notranslate">
if (isset($_GET['message']) &amp;&amp; isset($_SESSION[$_GET['message']])) {
	echo $_SESSION[$_GET['message']];
	unset($_SESSION[$_GET['message']]);
}
</pre>
<p>In this way, the message is displayed, but then unset so it&#8217;s not displayed again on refresh.</p>
<p>Simple and effective.<br />
<h3 class='related_post_title'>Related Posts:</h3>
<ul class='related_post'>
<li><a href='http://xavisys.com/howto-create-stream-csv-php/' title='Howto: Create and Stream a CSV with PHP'>Howto: Create and Stream a CSV with PHP</a></li>
<li><a href='http://xavisys.com/wordpress-25-shortcodes/' title='WordPress 2.5 Shortcodes'>WordPress 2.5 Shortcodes</a></li>
<li><a href='http://xavisys.com/wordpress-should-update-get_sidebar-to-work-with-multiple-sidebars/' title='Wordpress Should Update get_sidebar to Work With Multiple Sidebars'>WordPress Should Update get_sidebar to Work With Multiple Sidebars</a></li>
<li><a href='http://xavisys.com/cell-phone-safe-redirecting-with-mod_rewrite/' title='Cell phone safe redirecting with mod_rewrite'>Cell phone safe redirecting with mod_rewrite</a></li>
<li><a href='http://xavisys.com/redirecting-all-serveraliases-to-a-preferred-domain-with-mod_rewrite/' title='Redirecting all ServerAliases to a preferred domain with mod_rewrite'>Redirecting all ServerAliases to a preferred domain with mod_rewrite</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://xavisys.com/php-function-to-redirect-a-user-with-a-message/feed/</wfw:commentRss>
		<slash:comments>14</slash:comments>
		</item>
		<item>
		<title>Replace every other occurrence with str_replace</title>
		<link>http://xavisys.com/replace-every-other-occurrence-with-str_replace/</link>
		<comments>http://xavisys.com/replace-every-other-occurrence-with-str_replace/#comments</comments>
		<pubDate>Thu, 05 Jul 2007 19:08:41 +0000</pubDate>
		<dc:creator>Aaron D. Campbell</dc:creator>
				<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://xavisys.com/replace-every-other-occurrence-with-str_replace/</guid>
		<description><![CDATA[Well, someone put some code onto the str_replace PHP manual page to replace every other occurrence of a string with another string. However, it didn&#8217;t work, and was simply spam to get a link onto the PHP manual page (shame on them). So I made a function that DID work, for the poor lost souls [...]]]></description>
			<content:encoded><![CDATA[<p>Well, someone put some code onto the <a href="http://www.php.net/manual/en/function.str-replace.php">str_replace</a> PHP manual page to replace every other occurrence of a string with another string.  However, it didn&#8217;t work, and was simply spam to get a link onto the PHP manual page (shame on them).  So I made a function that DID work, for the poor lost souls who would be trying over and over to make that one work.  I figured that since I made it, I might as well post it, maybe someone needs something like this.</p>
<pre class="brush: php; title: ; notranslate">/**
* Replaces every other occurrence of search in haystack with replace
*
* @param $needle mixed
* @param $replace mixed
* @param $haystack mixed
* @param $count int[optional]
* @param $replace_first bool[optional] - Default true
* @return mixed
*/
function str_replace_every_other($needle, $replace, $haystack, &amp;$count=null, $replace_first=true) {
    $count = 0;
    $offset = strpos($haystack, $needle);
    //If we don't replace the first, go ahead and skip it
    if (!$replace_first) {
        $offset += strlen($needle);
        $offset = strpos($haystack, $needle, $offset);
    }
    while ($offset !== false) {
        $haystack = substr_replace($haystack, $replace, $offset, strlen($needle));
        $count++;
        $offset += strlen($replace);
        $offset = strpos($haystack, $needle, $offset);
        if ($offset !== false) {
            $offset += strlen($needle);
            $offset = strpos($haystack, $needle, $offset);
        }
    }
    return $haystack;
}

//Use it like this:
$str = &quot;one two one two one two&quot;;
echo str_replace_every_other('one', 'two', $str, $count).'\r\n';
//two two one two two two
echo str_replace_every_other('one', 'two', $str, $count, false).'\r\n';
//one two two two one two
</pre>
<p>I added the last option ($replace_first) to let you replace the odd occurrences (true, default) or the evens (false).<br />
<h3 class='related_post_title'>Related Posts:</h3>
<ul class='related_post'>
<li><a href='http://xavisys.com/howto-create-stream-csv-php/' title='Howto: Create and Stream a CSV with PHP'>Howto: Create and Stream a CSV with PHP</a></li>
<li><a href='http://xavisys.com/wordpress-25-shortcodes/' title='WordPress 2.5 Shortcodes'>WordPress 2.5 Shortcodes</a></li>
<li><a href='http://xavisys.com/wordpress-should-update-get_sidebar-to-work-with-multiple-sidebars/' title='Wordpress Should Update get_sidebar to Work With Multiple Sidebars'>WordPress Should Update get_sidebar to Work With Multiple Sidebars</a></li>
<li><a href='http://xavisys.com/php-function-to-redirect-a-user-with-a-message/' title='PHP function to Redirect a user with a message'>PHP function to Redirect a user with a message</a></li>
<li><a href='http://xavisys.com/php-pagination-with-mysqli/' title='PHP Pagination with mysqli'>PHP Pagination with mysqli</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://xavisys.com/replace-every-other-occurrence-with-str_replace/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>PHP Pagination with mysqli</title>
		<link>http://xavisys.com/php-pagination-with-mysqli/</link>
		<comments>http://xavisys.com/php-pagination-with-mysqli/#comments</comments>
		<pubDate>Fri, 15 Jun 2007 16:46:52 +0000</pubDate>
		<dc:creator>Aaron D. Campbell</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[mysqli]]></category>
		<category><![CDATA[pagination]]></category>

		<guid isPermaLink="false">http://xavisys.com/php-pagination-with-mysqli/</guid>
		<description><![CDATA[I&#8217;m asked pretty regularly for help with pagination in PHP. I have a nice mysqli pagination class that I use. Once it&#8217;s implemented, it is used like this: Now for the actual code of the class: Related Posts: Howto: Create and Stream a CSV with PHP WordPress 2.5 Shortcodes WordPress Should Update get_sidebar to Work [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m asked pretty regularly for help with pagination in PHP.  I have a nice mysqli pagination class that I use.  Once it&#8217;s implemented, it is used like this:</p>
<pre class="brush: php; title: ; notranslate">
/**
* $db is your mysqli connection
* $query is your query
* 'mailPage' is the $_GET variable to use for the pages.  It defaults to 'page' but using different values lets you have separate
* paginated data on that same web page.  Setting multiple paginated items on the same webpage to the same &quot;page&quot; variable
* will mean that when one goes to page two, all of them go to page 2.
*/
// Require the class
require_once('paginate.class.php');
// Lets not use your normal SELECT * FROM table query...just to show it still works
$query = &lt;&lt;&lt;
    SELECT
        `mail`.`id`,
        `mail2`.`read`,
        `mail2`.`seen`,
        `mail`.`from`,
        `mail`.`sent`,
        `mail`.`subject`,
        `mail`.`body`,
        `users`.`full_name` as from_name
    FROM `mail`
    JOIN `users`
        ON (`mail`.`from` = `users`.`id`)
    JOIN `mail2`
        ON (`mail`.`id` = `mail2`.`mess_id`)
    WHERE
        `mail2`.`uid`=1 &amp;&amp;
        `mail2`.`folder`=1
    ORDER BY
        `mail`.`sent` DESC
EOQ;

// Create our paginate object, and pass it the database, query, and page var
$paginate = new Paginate($db, $query, 'mailPage');
// Run the query
$r = $paginate-&gt;get_results();

// Loop through our results
while ($message = $r-&gt;fetch_object()) {
// Create your page of information
}

// Show the page links
echo $paginate-&gt;show_pages();
</pre>
<p><span id="more-36"></span></p>
<p>Now for the actual code of the class:</p>
<pre class="brush: php; title: ; notranslate">
&lt;?php
/**
 * paginate.class.php - PHP 5 class for pagination of mysqli results
 *
 * @author    Aaron D. Campbell &lt;aronCampbell@ezdispatch.com&gt;
 *
 * @version 1.0.4
 *
 * Changes:
 * 1.0.4:
 *   Added urlencoding to each get value when generating the links
 * 1.0.3:
 *   Added show_result_text() which displays a string like this: Showing __-__ of ___ total
 *   Changed show_pages() to return only the links without the containing div
 * 1.0.2:
 *   Added the ability to specify what $_GET variable is used to track the current page (allows for multiple instances on one webpage)
 *   Added &quot;first&quot; and &quot;last&quot; links to the page links
 * 1.0.1:
 *   Implemented more strict error checking on the page variable
 *   Changed PHP_SELF variables into SCRIPT_NAME to avoid vulnerabilities ( http://blog.phpdoc.info/archives/13-guid.html )
 * 1.0.0: original version
 */
class Paginate {
    //mysqli connection resource
    private $db;
    //MySQL query
    private $q;
    //number of rows per page.(default to 20)
    private $per_page = 20;
    //$_GET index to use for tracking page number
    private $page_var = 'page';
    //number of rows returned by the query.
    private $total_rows;
    //total number of pages.
    private $total_pages;
    //number of page links to show on either side of the current page.
    private $links_each_side = 3;
    //maximum number of pages to just show all, before using ellipses
    private $max_show_all = 10;

    /**
     * Set everything up.
     *
     * @param mysqli_resource    $db - mysqli connection resource
     * @param string $q - MySQL query
     * @param string[optional] $page_var - $_GET/$_SESSION['paginate_class'] index to use for &quot;Current Page&quot; defaults to 'page'
     *
     * @return void
     */
    public function __construct($db, $q, $page_var='page') {
        $this-&gt;db = $db;
        $this-&gt;q = $q;
        $this-&gt;page_var = $page_var;
        $this-&gt;check_page_var();
    }

    /**
     * Make sure that the current page is valid (whole number &gt;= 1)
     */
    public function check_page_var() {
        /*
        * if no page is specified, set it to 1.  If it IS specified, force it to int, and make sure
        * it is &gt;= 1
        */
        if (isset($_GET[$this-&gt;page_var]) &amp;&amp; (int)$_GET[$this-&gt;page_var] &gt; 0) {
            $_SESSION['paginate_class'][$this-&gt;page_var] = (int)$_GET[$this-&gt;page_var];
        } elseif (!isset($_SESSION['paginate_class'][$this-&gt;page_var])) {
            $_SESSION['paginate_class'][$this-&gt;page_var] = 1;
        }

        if ($this-&gt;per_page != 0) {
            $this-&gt;create_query();
        }
    }

    /**
     * Set the number of results returned per page.
     *
     * @param Unsigned Int $per_page
     */
    public function set_per_page($per_page) {
        $this-&gt;per_page = abs((int) $per_page);
    }

    /**
     * Set the number of page links to show on either side of the current page.
     *
     * @param Unsigned Int $links_each_side
     */
    public function set_links_each_side($links_each_side) {
        $this-&gt;links_each_side = abs((int) $links_each_side);
    }

    /**
     * Set maximum number of pages to just show all, before using ellipses
     * 0 will show all no matter what
     *
     * @param Unsigned Int $max_show_all
     */
    public function set_max_show_all($max_show_all) {
        $this-&gt;max_show_all = abs((int) $max_show_all);
    }

    /**
     * Used to get the result set, and set the Total rows.
     *
     * @return mysqli result object
     */
    public function get_results() {
        $return = $this-&gt;db-&gt;query($this-&gt;q);
        //get the number of rows that WOULD have been returned if there was no LIMIT
        //This is done by using FOUND_ROWS() after using SQL_CALC_FOUND_ROWS in the query
        $this-&gt;total_rows = array_pop($this-&gt;db-&gt;query('SELECT FOUND_ROWS()')-&gt;fetch_row());
        return $return;
    }

    /**
     * Show current results being viewed and total
     *
     * @return string - Showing __-__ of ___ total
     */
    public function show_result_text() {
        $start =<sup><a href="http://xavisys.com/php-pagination-with-mysqli/#footnote_0_36" id="identifier_0_36" class="footnote-link footnote-identifier-link" title="$_SESSION[&#039;paginate_class&#039;][$this-&amp;gt;page_var]-1) * $this-&amp;gt;per_page)+1;
        $end = (($start-1+$this-&amp;gt;per_page) &amp;gt;= $this-&amp;gt;total_rows)? $this-&amp;gt;total_rows:($start-1+$this-&amp;gt;per_page);
        return &amp;quot;Showing {$start}-{$end} of {$this-&amp;gt;total_rows} total&amp;quot;;
    }

    /**
     * Creates links to other pages.
     *
     * @return string - page links
     */
    public function show_pages() {
        //If number of rows per page is 0 (unlimited), return an empty string.
        if ($this-&amp;gt;per_page == 0) {
            return &#039;&#039;;
        }
        /*
        * If the user did not run get_results, we run it.  We could modify the query
        * to remove the SQL_CALC_FOUND_ROWS, and the limit...and either make it into
        * a count() query, or check num_rows...but it&#039;s a lot easier to just call
        * get_results with the query as it is.
        */
        if (!isset($this-&amp;gt;total_rows">1</a></sup> {
            $this-&gt;get_results();
        }
        //calculate the number of pages.
        $this-&gt;total_pages = ceil($this-&gt;total_rows/$this-&gt;per_page);

        //we use this array to store the page links that we want...so we can implode on |
        $page_array = array();

        $first = $this-&gt;get_page_link(1, '&amp;lt;&amp;lt; First');
        $last = ($this-&gt;total_pages &gt; 1)? $this-&gt;total_pages:1;
        $last = $this-&gt;get_page_link($last, 'Last &amp;gt;&amp;gt;');

        //if the number of pages is not more than the max that was specified, add
        //all the pages.
        if ($this-&gt;total_pages &lt;= $this-&gt;max_show_all || $this-&gt;max_show_all == 0) {
            for ($i=1; $i&lt;=$this-&gt;total_pages; $i++) {
                $page_array[] = $this-&gt;get_page_link($i);
            }
        } else {
            /*
            * make sure that page one gets in...but only if it wouldn't make it on
            * it's own...we don't want it there twice.
            */
            if($_SESSION['paginate_class'][$this-&gt;page_var] &gt;= $this-&gt;links_each_side+2) {
                $page_array[] = $this-&gt;get_page_link(1);
            }
            // If needed, add an ellipsis after page one.
            if($_SESSION['paginate_class'][$this-&gt;page_var] &gt;= $this-&gt;links_each_side+3) {
                $page_array[] = '...';
            }
            //Set the first page to be added (for pages in the main group)
            if ($_SESSION['paginate_class'][$this-&gt;page_var]-$this-&gt;links_each_side &lt;= 1) {
                $start = 1;
            } elseif ($_SESSION['paginate_class'][$this-&gt;page_var] &gt; $this-&gt;total_pages-$this-&gt;links_each_side) {
                $start = $this-&gt;total_pages-(2*$this-&gt;links_each_side);
            } else {
                $start = $_SESSION['paginate_class'][$this-&gt;page_var]-$this-&gt;links_each_side;
            }
            //Set the last page to be added (for pages in the main group)
            if ($_SESSION['paginate_class'][$this-&gt;page_var]+$this-&gt;links_each_side &gt;= $this-&gt;total_pages) {
                $end = $this-&gt;total_pages;
            } elseif ($_SESSION['paginate_class'][$this-&gt;page_var] &lt; $this-&gt;links_each_side+1) {
                $end = (2*$this-&gt;links_each_side)+1;
            } else {
                $end = $_SESSION['paginate_class'][$this-&gt;page_var]+$this-&gt;links_each_side;
            }
            //add the pages for the main group.
            for ($i=$start; $i&lt;=$end; $i++) {
                $page_array[] = $this-&gt;get_page_link($i);
            }
            // If needed, add an ellipsis before the last page.
            if($_SESSION['paginate_class'][$this-&gt;page_var] &lt;= $this-&gt;total_pages-$this-&gt;links_each_side-2) {
                $page_array[] = '...';
            }
            /*
            * make sure that the last page gets in...but only if it wouldn't make it
            * on it's own...we don't want it there twice.
            */
            if($_SESSION['paginate_class'][$this-&gt;page_var] &lt;= $this-&gt;total_pages-$this-&gt;links_each_side-1) {
                $page_array[] = $this-&gt;get_page_link($this-&gt;total_pages);
            }
        }
        //implode the links and ellipses into a | seperated string, and center in a div
        return &quot;{$first}&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;.implode(' | ',$page_array).&quot;&amp;nbsp;&amp;nbsp;&amp;nbsp;{$last}&quot;;
    }

    /**
     * Creates a page link, including the current $_GET string...updating only
     * $_SESSION['paginate_class'][$this-&gt;page_var].  It also returns only text (no link) if $p is the current
     * page.
     *
     * @param int $p - Page number to create link for
     * @return string - Link
     */
    private function get_page_link($p, $text=NULL) {
        if ($text === NULL) {
            $text = $p;
        }
        if ($p != $_SESSION['paginate_class'][$this-&gt;page_var]) {
            $_GET[$this-&gt;page_var] = $p;
            $get_string = array();
            foreach ($_GET as $k=&gt;$v) {
                if (is_array($v)) {
                    foreach ($v as $cur_v) {
                        $cur_v = urlencode($cur_v);
                        $get_string[] = &quot;{$k}[]={$cur_v}&quot;;
                    }
                } else {
                    $v = urlencode($v);
                    $get_string[] = &quot;{$k}={$v}&quot;;
                }
            }
            $get_string = implode('&amp;amp;',$get_string);
            return &quot;&lt;a href=\&quot;{$_SERVER['SCRIPT_NAME']}?{$get_string}\&quot;&gt;{$text}&lt;/a&gt;&quot;;
        } else {
            return $text;
        }
    }

    /**
     * Adds the proper LIMIT to the query, as well as adding SQL_CALC_FOUND_ROWS
     * which is used to get the total number of rows (ignoring LIMIT) without
     * doing a count() query
     */
    private function create_query() {
        //calculate the starting row
        $start = ($_SESSION['paginate_class'][$this-&gt;page_var]-1) * $this-&gt;per_page;
        //insert SQL_CALC_FOUND_ROWS and add the LIMIT
        $this-&gt;q = preg_replace('/^SELECT\s/i', 'SELECT SQL_CALC_FOUND_ROWS ', $this-&gt;q).&quot; LIMIT {$start},{$this-&gt;per_page}&quot;;
    }
}
?&gt;</pre>
<h3 class='related_post_title'>Related Posts:</h3>
<ul class='related_post'>
<li><a href='http://xavisys.com/howto-create-stream-csv-php/' title='Howto: Create and Stream a CSV with PHP'>Howto: Create and Stream a CSV with PHP</a></li>
<li><a href='http://xavisys.com/wordpress-25-shortcodes/' title='WordPress 2.5 Shortcodes'>WordPress 2.5 Shortcodes</a></li>
<li><a href='http://xavisys.com/wordpress-should-update-get_sidebar-to-work-with-multiple-sidebars/' title='Wordpress Should Update get_sidebar to Work With Multiple Sidebars'>WordPress Should Update get_sidebar to Work With Multiple Sidebars</a></li>
<li><a href='http://xavisys.com/php-function-to-redirect-a-user-with-a-message/' title='PHP function to Redirect a user with a message'>PHP function to Redirect a user with a message</a></li>
<li><a href='http://xavisys.com/replace-every-other-occurrence-with-str_replace/' title='Replace every other occurrence with str_replace'>Replace every other occurrence with str_replace</a></li>
</ul>
<ol class="footnotes"><li id="footnote_0_36" class="footnote">$_SESSION['paginate_class'][$this-&gt;page_var]-1) * $this-&gt;per_page)+1;
        $end = (($start-1+$this-&gt;per_page) &gt;= $this-&gt;total_rows)? $this-&gt;total_rows:($start-1+$this-&gt;per_page);
        return &quot;Showing {$start}-{$end} of {$this-&gt;total_rows} total&quot;;
    }

    /**
     * Creates links to other pages.
     *
     * @return string - page links
     */
    public function show_pages() {
        //If number of rows per page is 0 (unlimited), return an empty string.
        if ($this-&gt;per_page == 0) {
            return '';
        }
        /*
        * If the user did not run get_results, we run it.  We could modify the query
        * to remove the SQL_CALC_FOUND_ROWS, and the limit...and either make it into
        * a count() query, or check num_rows...but it's a lot easier to just call
        * get_results with the query as it is.
        */
        if (!isset($this-&gt;total_rows</li></ol>]]></content:encoded>
			<wfw:commentRss>http://xavisys.com/php-pagination-with-mysqli/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
	</channel>
</rss>

