Posts Tagged ‘PHP’

Posted by 6bytes at 21, February, 2013

Category: Kohana, PHP

Tags: , , , , , ,

Kohana framework displays really nice and descriptive error messages, for a development environment that is. For production obviously no error reports should be shown to the user. Instead we should display a pretty “404 Not Found”, or any other error page.
Creating those custom error pages in Kohana v3.x can be a real pain in the bum. I’m hoping that below guide will help you get this done in just few minutes.

Create HTML of your custom pages

/application/views/error/
/application/views/error/404.php

<h1>My custom 404 error page!</h1>

/application/views/error/500.php

<h1>My custom 500 error page!</h1>

Following the same pattern you can create custom pages for all error codes. If you want one error page for all just create it and later point all requests to that view.

Create template if you need it

Obviously you can use the same template you’re using for your normal pages. If so, feel free to omit this step.
/application/views/error.php

<?php echo $content; ?>

Extend Kohana exception class

Create directory and file as per below and copy code into the file.

/application/classes/kohana/exception.php

<?php defined('SYSPATH') or die('No direct script access.');

class Kohana_Exception extends Kohana_Kohana_Exception {

	public static function handler(Exception $e) {
		if (Kohana :: $environment === Kohana :: DEVELOPMENT) {
			// In development show normal Kohana errors
			parent :: handler($e);
		} else {
			// In production show custom error page
			try {
				Kohana :: $log->add(Log :: ERROR, parent :: text($e));

				$attributes = array (
					'action'  => 500,
					'message' => rawurlencode($e->getMessage())
				);

				if ($e instanceof HTTP_Exception) {
					$attributes['action'] = $e->getCode();
				}

				// Error sub-request.
				echo Request::factory(Route::get('error')->uri($attributes))->execute()->send_headers()->body();

			} catch (Exception $e) {
				// Clean the output buffer if one exists
				ob_get_level() and ob_clean();

				// Display the exception text
				echo parent :: text($e);

				// Exit with an error status
				exit (1);
			}
		}
	}

}

Add route for error pages

/application/bootstrap.php

Route::set('error', 'error/<action>/(<message>)', array('action' => '[0-9]++', 'message' => '.+'))
	->defaults(array(
		'controller' => 'error',
		'action'	 => '404'
));

Finally, create controller for error pages

/application/controller/error.php

<?php defined('SYSPATH') or die('No direct script access.');

class Controller_Error extends Controller_Template {
	// if you've created your custom error template. If not use your standard template here.
	public $template = 'error';
	
	public function before() {
		parent :: before();
	}
	
	/**
	 * Serves HTTP 404 error page
	 */
	public function action_404() {
		$this->template->content = View :: factory('error/404');
	}

	/**
	 * Serves HTTP 500 error page
	 */
	public function action_500() {
		$this->template->content = View :: factory('error/500');
	}
}

That’s it. Try to access a page on your site that doesn’t exist and you should see your custom 404 error page. Remember that when working in development standard Kohana errors will be show and only on production our pretty error pages will shine. Line 6 in `exception.php` controls this behaviour.

Above is simplified and slightly modified version of Lysender post.

Posted by 6bytes at 23, February, 2011

Category: Portfolio

Tags: , , , , ,

Just launched. TK Maxx and Red Nose Day campaign applications.

Facebook quiz

Fun quiz to find out your perfect Red Nose Day t-shirt.
View site

Website application

Upload you photo with Flash uploader, resize it and pan it around with JavaScript then save your results with PHP.
View site

Posted by 6bytes at 8, November, 2010

Category: PHP

Tags: , ,

A very simple PHP script to get almost live currency exchange rates from Yahoo Finance. I’m saying “almost” as as far as I know Yahoo updates it with some minor delay.

$from = 'GBP';
$to = 'USD';
$url = 'http://finance.yahoo.com/d/quotes.csv?f=l1d1t1&s='.$from.$to.'=X';
$handle = fopen($url, 'r');

if ($handle) {
	$result = fgetcsv($handle);
	fclose($handle);
}

echo '1 '.$from.' is worth '.$result[0].' '.$to.' Based on data on '.$result[1].' '.$result[2];

On the day of writing this post, above script will display:

1 GBP is worth 1.6125 USD Based on data on 11/8/2010 10:26am

Short explanation

I think everything is clear except for the URL and its parameters. Lets have a closer look.
In our example

$url = 'http://finance.yahoo.com/d/quotes.csv?f=l1d1t1&s='.$from.$to.'=X';

translates to

http://finance.yahoo.com/d/quotes.csv?f=l1d1t1&s=GBPUSD=X

Pasting this into your browser returns a csv file with three columns. Current exchange rate, date and time. Where:

  • l1 – exchange rate,
  • d1 – date,
  • t1 – time

If exact time is not what you’re looking for and all you need is just the exchange rate your url could look like this:

http://finance.yahoo.com/d/quotes.csv?f=l1&s=GBPUSD=X

I couldn’t find full list of parameters on yahoo but this site seems to have it all covered.

Yahoo finance not only gives you access to exchange rates but also stock data.

Posted by 6bytes at 14, April, 2010

Category: PHP

Tags: , ,

Recently I had a small problem with cURL. The request was supposed to return only JSON data to use in my app. I wrote the following:

$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, 'http://somedomain.com/api/123');
curl_setopt($curl, CURLOPT_HEADER, false);
curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_exec($curl);
$status = curl_getinfo($curl);
curl_close($curl);

The variable $status contained the API’s response in plain text and concatenated, inaccessible in this format, array with cURL’s status response. This made impossible for example checking if the request was successful. I had to get rid of the JSON response and leave only cURL status array. In order to do that I added curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); option which cleared all this garbage and left only cURL’s status array.

$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, 'http://somedomain.com/api/123');
curl_setopt($curl, CURLOPT_HEADER, false);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_exec($curl);
$status = curl_getinfo($curl);
curl_close($curl);

Last thing I had to do was retrieve API’s JSON response and assign it to another variable. I have to say the solution wasn’t my first logical choice. After some time it turned out that curl_exec() returns exactly what I need.
In the end, my bit of code became something like this:

$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, 'http://somedomain.com/api/123');
curl_setopt($curl, CURLOPT_HEADER, false);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
$response = curl_exec($curl);
$status = curl_getinfo($curl);
curl_close($curl);

Now, variable $response contained only JSON and $status contained only cURL’s response status. I was happy again.
 

Posted by 6bytes at 30, March, 2010

Category: MySQL, PHP

Tags: , , , ,

The bug

If you’re running PHP 5.1.6 and just started using PDO for your database connection, it’s likely you’ll run into quite an annoying bug.
Lets test a simple query partly taken from PHP documentation.

$calories = 150;
$colour = 'red';
$sth = $myPDO->prepare('SELECT name, colour, calories
    FROM fruit
    WHERE calories < :calories AND colour = :colour');
$sth->bindParam(':calories', $calories, PDO::PARAM_INT);
$sth->bindParam(':colour', $colour, PDO::PARAM_STR, 12);
$sth->execute();
if ($sth->rowCount() >= 1) {
    // iterate through results
}

Everything seems great. The problem is that $sth->rowCount() will always return 0. No matter how many results your query returns, the value of rowCount() will always be 0.

Full bug report can be found here: http://bugs.php.net/40822.

Solution

Upgrade PHP :)

If that’s not an option read on.

Lets create our own simple PDO and PDOStatement classes.

class myPDO extends PDO {
	function __construct($name_host, $username='', $password='', $driverOptions=array()) {
		parent::__construct($name_host, $username, $password, $driverOptions);
		$this->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('myPDOStatement', array($this)));
	}
}

class myPDOStatement extends PDOStatement {
	public $db;

	protected function __construct($db) {
		$this->db = $db;
	}
}

Above will not fix anything. This is just a start so we can overload some PDO methods to apply the fix.

To fix the bug we need to tell MySQL to use the buffered versions of the MySQL API by setting attribute MYSQL_ATTR_USE_BUFFERED_QUERY to true.
For some reason, still unknown to me setting this option like that

class myPDO extends PDO {
	function __construct($name_host, $username='', $password='', $driverOptions=array()) {
		parent::__construct($name_host, $username, $password, $driverOptions);
		$this->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('myPDOStatement', array($this)));
		$this->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);
	}
}

will not work.

After many hours of tearing my hair out I found out that all you need to do is this:

class myPDO extends PDO {
	function __construct($name_host, $username='', $password='', $driverOptions=array()) {
		$driverOptions[PDO::MYSQL_ATTR_USE_BUFFERED_QUERY] = true;
		parent::__construct($name_host, $username, $password, $driverOptions);
		$this->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('myPDOStatement', array($this)));
	}
}

You have to set MYSQL_ATTR_USE_BUFFERED_QUERY before instantiating a connection. Setting it after the connection was made will not work.

When this is done we need to modify our myPDOStatement class.

class myPDOStatement extends PDOStatement {
	public $db;
	// other attributes
	private $foundRows; // will hold number of affected rows

	protected function __construct($db) {
		$this->db = $db;
	}

	public function execute($array = null) {
		if ($array === null) {
			$result = parent :: execute();
		} else {
			$result = parent :: execute($array);
		}

		// fix for PHP 5.1.6 rowCount error
		$this->foundRows = $this->db->query("SELECT FOUND_ROWS()")->fetchColumn();

		return $result;
	}

	public function rowCount() {
		return $this->foundRows;
	}
}

After above changes our rowCount() method will return proper values.
 

Posted by 6bytes at 16, March, 2010

Category: PHP

Tags: , ,

In last two posts I described how to install memcached daemon and memcache extension for PHP. Today I’ll show you how to configure your PHP to use memcache to handle sessions.

One thing I should mention is that when installing memcache you should answer yes when asked

1. Enable memcache session handler support? : yes

First of all lets start two memcached processes just in case one crashes, we’ll have a second one ready right away.

memcached -u root -d -m 512 -l 127.0.0.1 -p 11211
memcached -u root -d -m 512 -l 127.0.0.1 -p 11212

Worth mentioning is that when one memcached process crashes session information will not be transferred to another. New session will be started.

Next edit your php.ini file. Comment the line with your current session handler setting

; session.save_handler = files

and add two new lines

session.save_handler = memcache
session.save_path = "tcp://localhost:11211, tcp://localhost:11212"

If for any reason you don’t want to edit php.ini file you can set those options directly in your PHP script.

ini_set('session.save_handler', 'memcache');
ini_set('session.save_path', 'tcp://localhost:11211, tcp://localhost:11212');

Restart your web server

service httpd restart

For full list of runtime configuration options go here.

Posted by 6bytes at 13, March, 2010

Category: CentOS, Linux, PHP

Tags: , , ,

After installing memcached daemon we have to install memcache PHP extension.

yum install php-pecl-memcache

or

apt-get install php5-memcache

If above is not an option try below method.

pecl install memcache

After successful installation add memcache.so extension to your php.ini file.

vim /etc/php.ini

Add this line:

extension=memcache.so

Restart web server

service httpd restart

Test out our installation.

<?php
$memcache = new Memcache;
$memcache->connect('localhost', 11211) or die ('Can\'t connect!');
$version = $memcache->getVersion();
echo 'Server version: '.$version;

You should see your server’s version number. If you see a blank page make sure that memcached daemon is working. If you’re getting “Can’t connect!” message despite running daemon try changing ‘localhost’ to ‘127.0.0.1’.

Posted by 6bytes at 25, June, 2009

Category: Apache, MySQL, PHP

Tags: , , ,

If your PDO scripts are crashing Apache after installing xampp 1.7 for windows all you need to do is:

All should be good now.