CakePHP RequestHandler Extended

A CakePHP component which extends built-in RequestHandler component by adding some useful geolocation information. It requires MaxMind WebService license key to work properly.

Save it with the name request_handler_ext.php into your application components directory under controllers.

You can call the following methods within your controller (e.g. $this->RequestHandlerExt->methodName()):

getClientCountryCode() : ISO 3166 Two-letter Country Code
getClientRegionCode() : Region Code
getClientCity() : City
getClientPostalCode() : Postal Code
getClientLatitude() : Latitude
getClientLongitude() : Longitude
getClientMetropolitanCode() : Metropolitan Code
getClientMetropolitanCode() : Area Code
getClientIsp() : ISP
getClientOrganization() : Organization

<?php
/**
 * Extends RequestHandler component by adding some useful geolocation information.
 *
 * Requires MaxMind WebService license key to work properly
 *
 * @copyright     2009 Erhan Abay
 * @package       app
 * @subpackage    app.controllers.components
 * @version       $Revision$
 * @lastmodified  $Date$
 */
App::import('Component', 'RequestHandler');
class RequestHandlerExtComponent extends RequestHandlerComponent
{
	/**
	 * Required to query MaxMind WebService
	 *
	 * Provide your own key by replacing XXXXX.
	 */
	const MM_LICENSE_KEY = 'XXXXXX';
	public function startup(&amp;amp;amp;amp;$controller)
	{
		parent::startup(&amp;amp;amp;amp;$controller);
		$this->controller =&amp;amp;amp;amp; $controller;
	}
	/**
	 * Searches key value in array returned by function getGeoLocation()
	 *
	 * @param string $name Name of the method
	 * @param unknown_type $arguments Not used, required not to give error
	 * @return string if key found
	 *         null else
	 */
	public function __call($name, $raw = false)
	{
		$var = Inflector::underscore(preg_replace('/getClient/i', '', $name));
		$geo_location = (array)$this->getGeoLocation($raw);
		return array_key_exists($var, $geo_location) ? $geo_location[$var] : null;
	}
	/**
	 * Queries to the MaxMind WebService and returns an array of information
	 *
	 * @param bool $raw
	 * @return null if IP address is local
	 *         bool false if webservice returns error code
	 *         string if $raw is set true
	 *         array else
	 */
  public function getGeoLocation($raw = false)
  {
  	if ($this->isLocalIP()) {
  		return null;
  	}
  	if ($this->controller->Session->check('User.GeoLocation')) {
  		return $raw ? $this->controller->Session->read('User.GeoLocation.raw') : $this->controller->Session->read('User.GeoLocation');
  	}
    App::import('HttpSocket');
    $http = new HttpSocket();
    /*
     * Returns in order:
     *
     * 0  ISO 3166 Two-letter Country Code,
     * 1  Region Code,
     * 2  City,
     * 3  Postal Code,
     * 4  Latitude,
     * 5  Longitude,
     * 6  Metropolitan Code,
     * 7  Area Code,
     * 8  ISP,
     * 9  Organization,
     * 10 Error code
     */
    $result = $http->get('http://geoip1.maxmind.com/f', array(
      'l' => self::MM_LICENSE_KEY,
      'i' => $this->getClientIP()
    ));
    $values = explode(',', $result);
    if (isset($values[10])) {
      return false;
    }
    if ($raw) {
    	return $result;
    }
    $keys = array('country_code', 'region_code', 'city', 'postal_code', 'latitude', 'longitude', 'metropolitan_code', 'area_code', 'isp', 'organization');
    $data = array_combine($keys, $values);
    $data['coords'] = $values[4].','.$values[5];
    $data['raw'] = $result;
    $this->controller->Session->write('User.GeoLocation', $data);
    return $data;
  }
  /**
   * Detects whether IP address is local or not
   *
   * @param string $ip IP address to check
   * @return bool
   */
  public function isLocalIP($ip = null) {
  	$ip = is_null($ip) ? $this->getClientIP() : $ip;
  	$regex = '/(192\.168\.[0-9]{1,3}\.[0-9]{1,3})';
	$regex .= '|(10\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})';
	$regex .= '|(172\.0?([1][6-9])¦([2][0-9])¦([3][0-1])\.[0-9]{1,3}\.[0-9]{1,3})';
	$regex .= '|(127\.0\.0\.1)/';
  	return (bool)preg_match($regex, $ip);
  }
}
?>

Make CakePHP Email Component Reusable

I know the title of this post a little bit confusing but let me explain what I want to tell.

You are using CakePHP‘s email component and you should set the same information in each time before you send your email like server address, username, password etc.

Just create a new file mailer.php with the following content and drop it into your CakePHP application components folder (I like convention over configuration! ;) ).

<?php
App::import('Component', 'Email');
class MailerComponent extends EmailComponent
{
  var $from = 'ME <me@localhost>';
  var $replyTo = 'noreply@localhost';
  var $sendAs = 'both';
  var $delivery = 'smtp';
  var $xMailer = 'Postman';
  var $smtpOptions = array(
    'port'=> 25,
    'host' => 'serveradress',
    'timeout' => 30,
    'username' => 'username',
    'password' => 'password'
  );
}
?>

And right now you have a new component with the name “Mailer” and its server configuration is predefined. You can reuse it without being affected by any kind of mail server change.

You can define a new function inside your controller (_sendEmail() in our case) and make the email sending process more painless.

class AnyController extends AppController
{
  function contact()
  {
    if ($this->_sendEmail('Name', 'blabla@fakeemail', 'Grate site!')) {
      $this->Session->setFlash(__("Thank you", true));
    } else {
      $this->Session->setFlash('Damn it!');
    }
  }
  function _sendEmail($name, $email, $message)
  {
    $this->Mailer->to = 'info@localhost';
    $this->Mailer->subject = __("Site Contact", true);
    $this->Mailer->template = 'contact';
    $this->set('name', $name);
    $this->set('email', $email);
    $this->set('message', $message);
    $this->Mailer->send();
    $this->log( $this->Mailer->subject . ' -> Name:'. $name .' | E-posta: '. $email .' | Message: '. $message .' | smtp error: '. serialize($this->Mailer->smtpError) );
    return $this->Mailer->smtpError ? false : true;
  }
}

That’s all! Check Bakery for other cakes :)