How to identify your clients` browser type

Posted by Dan Sosedoff on October 02, 2009

Some time ago i wrote a simple regex patterns to determine whether my client crawler bot, mobile client or just regular one. Easy to expand and to use.

function is_mobile($agent) {
    $pattern = '/(blackberry|motorokr|motorola|sony|windows ce|240x320|176x220|palm|mobile|iphone|ipod|symbian|nokia|samsung|midp)/i';
    return (bool)preg_match($pattern, $agent);
}
 
function is_crawler($agent) {
    pattern = '/(google|yahoo|baidu|bot|webalta|ia_archiver)/';
    return (bool)preg_match($pattern, $agent);
}

Rails-like PHP url router

Posted by Dan Sosedoff on September 20, 2009

My previous version of php url router was not good enough, so i`ve done some core modification to the class. Its more flexible now. The whole idea is very similar to Rails` and Merb`s url router (merb is a rails-fork). In fact, previous version doesn`t even support GET variables in the request uri. New version just merges variables from request string into the same parameters array where other variables are stored. Also there is a command “default_routes” that maps default routes like this “/:controller/:action/:id”. So, basically you have two options – use defaults & custom routes or use only custom, which means that all other requests will be ignored. Function default_routes must be declared last.

Here is the sources:

define('ROUTER_DEFAULT_CONTROLLER', 'home');
define('ROUTER_DEFAULT_ACTION', 'index');
 
class Router {
  public $request_uri;
  public $routes;
  public $controller, $controller_name;
  public $action, $id;
  public $params;
  public $route_found = false;
 
  public function __construct() {
    $request = $_SERVER['REQUEST_URI'];
    $pos = strpos($request, '?');
    if ($pos) $request = substr($request, 0, $pos);
 
    $this->request_uri = $request;
    $this->routes = array();
  }
 
  public function map($rule, $target=array(), $conditions=array()) {
    $this->routes[$rule] = new Route($rule, $this->request_uri, $target, $conditions);
  }
 
  public function default_routes() {
    $this->map('/:controller');
    $this->map('/:controller/:action');
    $this->map('/:controller/:action/:id');
  }
 
  private function set_route($route) {
    $this->route_found = true;
    $params = $route->params;
    $this->controller = $params['controller']; unset($params['controller']);
    $this->action = $params['action']; unset($params['action']);
    $this->id = $params['id']; 
    $this->params = array_merge($params, $_GET);
 
    if (empty($this->controller)) $this->controller = ROUTER_DEFAULT_CONTROLLER;
    if (empty($this->action)) $this->action = ROUTER_DEFAULT_ACTION;
    if (empty($this->id)) $this->id = null;
 
    $w = explode('_', $this->controller);
    foreach($w as $k => $v) $w[$k] = ucfirst($v);
    $this->controller_name = implode('', $w);
  }
 
  public function execute() {
    foreach($this->routes as $route) {
      if ($route->is_matched) {
        $this->set_route($route);
        break;
      }
    }
  }
}
 
class Route {
  public $is_matched = false;
  public $params;
  public $url;
  private $conditions;
 
  function __construct($url, $request_uri, $target, $conditions) {
    $this->url = $url;
    $this->params = array();
    $this->conditions = $conditions;
    $p_names = array(); $p_values = array();
 
    preg_match_all('@:([\w]+)@', $url, $p_names, PREG_PATTERN_ORDER);
    $p_names = $p_names[0];
 
    $url_regex = preg_replace_callback('@:[\w]+@', array($this, 'regex_url'), $url);
    $url_regex .= '/?';
 
    if (preg_match('@^' . $url_regex . '$@', $request_uri, $p_values)) {
      array_shift($p_values);
      foreach($p_names as $index => $value) $this->params[substr($value,1)] = urldecode($p_values[$index]);
      foreach($target as $key => $value) $this->params[$key] = $value;
      $this->is_matched = true;
    }
 
    unset($p_names); unset($p_values);
  }
 
  function regex_url($matches) {
    $key = str_replace(':', '', $matches[0]);
    if (array_key_exists($key, $this->conditions)) {
      return '('.$this->conditions[$key].')';
    } 
    else {
      return '([a-zA-Z0-9_\+\-%]+)';
    }
  }
}

Setup example:

$r = new Router(); // create router instance 
 
$r->map('/', array('controller' => 'home')); // main page will call controller "Home" with method "index()"
$r->map('/login', array('controller' => 'auth', 'action' => 'login'));
$r->map('/logout', array('controller' => 'auth', 'action' => 'logout'));
$r->map('/signup', array('controller' => 'auth', 'action' => 'signup'));
$r->map('/profile/:action', array('controller' => 'profile')); // will call controller "Profile" with dynamic method ":action()"
$r->map('/users/:id', array('controller' => 'users'), array('id' => '[\d]{1,8}')); // define filters for the url parameters
 
$r->default_routes();
$r->execute();

Usage example:

$router = new Router();
// ... some configs ...
$controller = $router->controller; // will return name as it appears in url, ex: 'user_images'
$controller = $router->controller_name; // will return processed name of controller
// for example, if class name in url is 'user_images', then 'controller_name' var will be UserImages
$router->action;
$router->id; // if parameter :id presents
$router->params; // array(...)
$router->route_matched; // true - if route found, false - if not

Small and useful class.

Simple php url routing controller 1

Posted by Dan Sosedoff on July 04, 2009

!!! CONSIDER THIS AS AN UPDATE:Rails-like PHP Url Router

Here is simple url parser, that produces output parameters lile: controller, action, parameters. Also, it support simple virtual key rules, “rails” style.

For example, url like “http://foo.bar/book/view/12″ will produce parameters: controller = book, action = view, id = 12.

Here is php code:

<?
 
/* ------------------------------------------------------------- */
/* URL Router class */
/* ------------------------------------------------------------- */
 
class Router {
  static protected $instance;
  static protected $controller;
  static protected $action;
  static protected $params;
  static protected $rules;
 
  public static function getInstance() {
    if (isset(self::$instance) and (self::$instance instanceof self)) {
      return self::$instance;
    } else {
      self::$instance = new self();
      return self::$instance;
    }
  }
 
  private static function arrayClean($array) {
    foreach($array as $key => $value) {
      if (strlen($value) == 0) unset($array[$key]);
    }  
  }
 
  private static function ruleMatch($rule, $data) {    
    $ruleItems = explode('/',$rule); self::arrayClean(&$ruleItems);
    $dataItems = explode('/',$data); self::arrayClean(&$dataItems);
 
    if (count($ruleItems) == count($dataItems)) {
      $result = array();
 
      foreach($ruleItems as $ruleKey => $ruleValue) {
        if (preg_match('/^:[\w]{1,}$/',$ruleValue)) {
          $ruleValue = substr($ruleValue,1);
          $result[$ruleValue] = $dataItems[$ruleKey];
        }
        else {
          if (strcmp($ruleValue,$dataItems[$ruleKey]) != 0) {
            return false;
          }
        }
      }
 
      if (count($result) > 0) return $result;
      unset($result);
    }
    return false;
  }
 
  private static function defaultRoutes($url) {
    // process default routes
    $items = explode('/',$url);
 
    // remove empty blocks
    foreach($items as $key => $value) {
      if (strlen($value) == 0) unset($items[$key]);
    }
 
    // extract data
    if (count($items)) {
      self::$controller = array_shift($items);
      self::$action = array_shift($items);
      self::$params = $items;
    }
  }
 
  protected function __construct() {
    self::$rules = array();
  }
 
  public static function init() {
    $url = $_SERVER['REQUEST_URI'];
    $isCustom = false;
 
    if (count(self::$rules)) {
      foreach(self::$rules as $ruleKey => $ruleData) {
        $params = self::ruleMatch($ruleKey,$url);
        if ($params) {          
          self::$controller = $ruleData['controller'];
          self::$action = $ruleData['action'];
          self::$params = $params;
          $isCustom = true;
          break;
        }
      }
    }
 
    if (!$isCustom) self::defaultRoutes($url);
 
    if (!strlen(self::$controller)) self::$controller = 'home';
    if (!strlen(self::$action)) self::$action = 'index';
  }
 
  public static function addRule($rule, $target) {
    self::$rules[$rule] = $target;
  }
 
 
  public static function getController() { return self::$controller; }
  public static function getAction() { return self::$action; }
  public static function getParams() { return self::$params; }
  public static function getParam($id) { return self::$params[$id]; }
}
?>

Basic usage:

$router = Router::getInstance(); // init router
$router->addRule('/books/:id/:keyname',array('controller' => 'books', 'action' => 'view')); // add simple rule
// add some more rules
$router->init(); // execute router

So, as you can see, the router class can be accessible from anywhere in your code. Just because the class has beed designed as one-instance object, you cannot call for new instance. To get instance of router you need to call Router::getInstance() instead.
We have added simple rule for url`s like this ‘/books/12345/this-is-a-book-keyname’. Default routing system will identify this url incorrectly (controller => books, action => 12345). So the action 12345 not exists. That`s why we need to add custom rule. It will parse this url correctly: controller => books, action => view. And the rest of parameters will be automatically added to parameteres array. To access this array you need to call $router->getParams(); If you need to get just one parameter, call directly: $var = $router->getParam(0).

This router is really simple and can be used in small web sites that not require complete framework. Also it is fast.

Download file Router.php
View paste

Scaling images with ImageMagick and PHP 1

Posted by Dan Sosedoff on March 20, 2009

Here is php version of ruby class that i made a long time ago. The same functionality and results.

<?
class ImageScale {
 
	private function changeGeometry($sz,$value) {
		if ($sz['width'] > $sz['height']) { // horizontal image
			$newsz['width'] = $value;
			$newsz['height'] = ceil(($newsz['width'] * $sz['height']) / $sz['width']);
		}
		else { // vertical image
			$newsz['height'] = $value;
			$newsz['width'] = ceil(($newsz['height'] * $sz['width']) / $sz['height']);
		}
		return $newsz;	
	}
 
	private function changeGeometry2($sz,$value) {
		$newsz['width'] = $value;
		$newsz['height'] = ceil(($newsz['width'] * $sz['height']) / $sz['width']);
		return $newsz;
	}
 
	/**
	 * 	Make thumbnail of specified maximum side size
	 */
	public function processThumb($source_path, $dest_path, $sidesize, $quality=85, $scale_method=1) {
		if (file_exists($source_path) && is_readable($source_path)) {
			$image = new Imagick($source_path);
 
			$geometry = $image->getImageGeometry(); // ['width', 'height']
			if ($geometry['width'] > $sidesize) {
				if ($scale_method == 1) $geometry = $this->changeGeometry($geometry,$sidesize);
				if ($scale_method == 2) $geometry = $this->changeGeometry2($geometry,$sidesize);
			}
			$image->cropThumbnailImage($geometry['width'],$geometry['height']);
			$image->setCompression(Imagick::COMPRESSION_JPEG);
			$image->setCompressionQuality($quality);
			return $image->writeImage($dest_path);
		}
		return false;
	}	
 
	/**
	 * 	Make square thumbnail
	 */
	public function processRectThumb($source_path, $dest_path, $size=150, $quality=85) {
		if (file_exists($source_path) && is_readable($source_path)) {
			$image = new Imagick($source_path);
 
			if ($image) {
				$image->cropThumbnailImage($size,$size);
				$image->setCompression(Imagick::COMPRESSION_JPEG);
				$image->setCompressionQuality($quality);
				return $image->writeImage($dest_path);
			}
		}
		return false;
	}
}
?>

Download file – http://files.sosedoff.com/1167c8db/