Category Archives: PHP

Using xDebug’s Clickable Stack trace with Programmer’s Notepad (and MINGW32) in Windows

I’ve spent far more time trying to make this work than I care to admit. The mistake I’ve made in the past is to try and do this in a batch file, I’ve now done it in bash (which I have anyway since I installed Git and asked for unix-tools to be globally available).

xDebug lets you turn file names into click able links, which can in turn be used to launch your text editor in the right place.

Requirements:

  • bash & sed – These come when you install Git and ask for the Unix tools to be globally available
  • Programmers Notepad – naturally. Though this is easy enough to tweak to any editor.
  • Firefox and xDebug

Step 1 – Create the SH file

Put this text into a file called “EditWithPnotepad.sh” and save it somewhere sensible. You may need to tweak the last line, especially if you want to use this for an editor other than PNotepad.

#!/bin/bash
uo=$1
protocol=$(echo $uo | sed 's/\([a-z]*\):\/\/\(.*\):\([0-9]*\)\//\1/');
file=$(echo $uo | sed 's/\([a-z]*\):\/\/\(.*\):\([0-9]*\)\//\2/');
line=$(echo $uo | sed 's/\([A-z]*\):\/\/\(.*\):\([0-9]*\)\//\3/');
"C:\Program Files (x86)\Programmer's Notepad\pn.exe" --line $line "$file" &

Step 2 – Register the protocol

Open regedit and create the following keys – we’ll set the values after. Do this by right clicking on the parent (HKEY_CLASSES_ROOT, then pontepad etc) and clicking on New > Key.

[HKEY_CLASSES_ROOT\pnotepad]
[HKEY_CLASSES_ROOT\pnotepad\shell]
[HKEY_CLASSES_ROOT\pnotepad\shell\open]
[HKEY_CLASSES_ROOT\pnotepad\shell\open\command]

Click on pnotepad and the double click on (default) to change it. Set it to “URL: PNotepad Protocol”.

Under that same (default) key right click in the blank space and click on New > Key and name it “URL Protocol”. It needs no value.

Now click on Command and set its (default) to your own version of the following, be careful not to mess up the quotes or spaces.

"C:\Program Files (x86)\Git\bin\sh.exe" "D:\Path\To\Your\EditWithPnotepad.sh" "%1"

Step 3 – Tell xDebug how to format URLs

Open up your php.ini and add or edit this line, then restart PHP/Apache as required.

xdebug.file_link_format="pnotepad://%f:%l"

Step 4 – Test

That should be it, make some code code throw an error and try clicking the link.

Any touble, or updates let me know!

Categories:

Magical Image Resizing URL’s with Zend_Framework (and cached for Apache)

As a lazy programmer I don’t like resizing images all the time. So I made this bit of code to let me write a URL including the file size and have the work done for me.

  • Written for Zend Framework 1.11.12
  • It will resize the image and save it into the correct location so that Apache will find it next time and not call the PHP.
  • You can specify the required width, height, both or neither.
  • If a width or height is given as 0 then the original dimension will be used.

Add some settings to application.ini

cache.publicDir = '/cache'
cache.dataDir = APPLICATION_PATH "/../public/cache"
data.dataDir = APPLICATION_PATH "/../public/data

Add a route in the Bootstrap

The regex router must match a URL containing the required file sizes, the file name and as many sub directories as thrown at it. It matches the following and sends them to the ThumbAction on FilesController:

  • /cache/100/200/file.jpg
  • /cache/100/200/subdir/file.jpg
  • /cache/100/200/another-subdir/file.jpg
$router->addRoute('file_resized_thumbnail', new Zend_Controller_Router_Route_Regex(
	'cache/([0-9]*)/([0-9]*)(.*/?)/([^/]*?)',
		array(
			'controller'	=> 'files',
			'action'		=> 'thumb',
		),
		array(
			1	=> 'width',
			2	=> 'height',
			3	=> 'subdir',
			4	=> 'file'
		),
		'/cache/%d/%d/%s/%s'
));

FilesController.php::ThumbAction()

/**
*	Loads the file, gets a resized version and saves it in a location identical to 
*	the current URL so Apache can find it next time.
**/
public function thumbAction()
{
	$this->view->layout()->disableLayout();
	$options = $this->_helper->container->get('options');
	
	$fileName = $this->getRequest()->getParam('file');
	$subdir = $this->getRequest()->getParam('subdir');
	$width = $this->getRequest()->getParam('width');
	$height = $this->getRequest()->getParam('height');
	
	$cacheUrlPrefix = $options['cache']['publicDir'];
	$cachePathPrefix = $options['cache']['dataDir'];
	$dataPathPrefix = $options['data']['dataDir'];
	
	$fileName = urldecode($fileName);
	$subdir = ($subdir == "") ? "" : urldecode($subdir);
	
	$fullPath = $dataPathPrefix.'/'.$subdir.$fileName;
	
	if(realpath($fullPath) == false){
		throw new Zend_Controller_Action_Exception('File not Found: '.$fullPath, 404);
	}
	$file = new Pata_File(array(
		'path'			=>	$fullPath
	));
	
	if ($file == false) {
		throw new Zend_Controller_Dispatcher_Exception('That file was not found in that subdir');
	}
	$cacheDir = $cachePathPrefix.'/'.$width.'/'.$height.$subdir;
	$cacheFile = $cacheDir.'/'.$fileName;
	if (file_exists($cacheDir) == false) {
		mkdir($cacheDir, 0777, true);
	}
	imagepng($file->getThumbnailPng($width, $height), $cacheFile);
	
	header('content-type: image/png');
	readfile($cacheFile);
}

library/Pata/File.php

/*
*	This is a model I use to make working with files a bit easier. Several of the functions are used by thumbAction but the code could be moved to the controller if you so desired.
*/
<?php
class Pata_File
{
	protected $path;
	protected $relativePath;
	protected $url;
	protected $image;
	protected $thumbUrl;
	protected $thumbnailDimensions = array();

	protected $thumbnailPng = array();
	
	protected $description;
	
	public function __construct($options)
	{
		if (isset($options['path'])) {
			$this->path = $options['path'];
		}
		if (isset($options['relativePath'])) {
			$this->relativePath = $options['relativePath'];
		}
		if (isset($options['url'])) {
			$this->url = $options['url'];
		}
		if (isset($options['thumbUrl'])) {
			$this->thumbUrl = $options['thumbUrl'];
		}
	}

	public function getName()
	{
		return basename($this->getPath());
	}
	
	public function getPath()
	{
		if ($this->path == null) {
			throw new Exception('No path has been set');
		}
		return $this->path;
	}
	
	public function getRelativePath()
	{
		return $this->relativePath;
	}
	
	public function getDir()
	{
		return substr($this->getPath(), 0, - strlen($this->getName()));
	}
	
	public function setDescription($description)
	{
		$this->description = $description;
	}
	
	public function getDescription()
	{
		return $this->description;
	}
	
	/**
	 * The URL to the actual file itself - not a page displaying it, see getPageUrl()
	 * @return type 
	 */
	public function getUrl($width = 800, $height = null)
	{
		if ($this->url == null) {
			throw new Exception('No url has been set');
		}
		$height = $height ?: round($width * 0.75, 0);
		return str_replace(array(':width:', ':height:'), array($width, $height), $this->url);
	}
	
	public function getThumbUrl($width = 120, $height = null)
	{
		if ($this->thumbUrl == null) {
			throw new Exception('No thumbUrl has been set');
		}
		$height = $height ?: round($width * 0.75, 0);
		return str_replace(array(':width:', ':height:'), array($width, $height), $this->thumbUrl);
	}

	public function setPath($new)
	{
		$this->path = $new;
	}
	
	public function setUrl($new)
	{
		$this->url = $new;
	}
	
	public function setThumbUrl($new)
	{
		$this->thumbUrl = $new;
	}
	
	public function setPageUrl($new)
	{
		$this->pageUrl = $new;
	}
	
	/**
	 *	Page Url is the URL to the page on which this image can be viewed
	 */
	public function getPageUrl()
	{
		if ($this->pageUrl == null) {
			throw new Exception('No pageUrl has been set');
		}
		return $this->pageUrl;
	}
	
	public function getExtention()
	{
		if (preg_match('/\.[A-z0-9]+$/', $this->getPath(), $ext)) {
			$ext = $ext[0];
			$ext = substr($ext, 1);
		} else {
			$finfo = new finfo(FILEINFO_MIME);
			$mime = $finfo->file($this->getPath());
			$mime = explode(";", $mime);
			$mime = $mime[0];
			$mimeToExt = array(
				"text/plain" => "txt",
			);
			if (isset($mimeToExt[$mime])) {
				$ext = $mimeToExt[$mime];
			} else {
				$ext = "";
			}
		}
		return $ext;
	}
	
	public function freeMemory()
	{
		imagedestroy($this->getImage());
		$this->img = null;
	}
	
	public function getImage()
	{
		if ($this->image == null) {
			switch(strtolower($this->getExtention())){
				case "jpg":
				case "jpeg":
					$this->img = imagecreatefromjpeg($this->getPath());
					break;
				case "png":
					$this->img = imagecreatefrompng($this->getPath());
					break;
				case "gif";
					$this->img = imagecreatefromgif($this->getPath());
					break;
				case "bmp";
					$this->img = imagecreatefrombmp($this->getPath());
					break;
				case ".txt";
				case "doc";
					$this->img = imagecreatefrompng(realpath(dirname(__FILE__).'/File/Images/thumb_doc.png'));
					break;
				case "xls";
					$this->img = imagecreatefrompng(realpath(dirname(__FILE__).'/File/Images/thumb_excel.png'));
					break;
				default;
					$this->img = imagecreatefrompng(realpath(dirname(__FILE__).'/File/Images/thumb_unknown.png'));
					break;
			}
		}
		return $this->img;
	}
	
	public function getThumbnailDimensions($width, $height, $return = 'both')
	{
		$cacheName = $width.'x'.$height.$return;
		if (isset($this->thumbnailDimensions[$cacheName])) {
			return $this->thumbnailDimensions[$cacheName];
		}
		
		if ($this->getType() == 'images') {
			$imgSize = getimagesize($this->getPath());
			$imgWidth = $imgSize[0];
			$imgHeight = $imgSize[1];
		} else {
			$img = $this->getImage();
			$imgWidth = imagesx($img);
			$imgHeight = imagesy($img);
		}
		if ($width > 0) {
			$thumbWidth = $width;
		} else {
			$thumbWidth = ($imgWidth / $imgHeight) * $height;
		}
		if ($height > 0) {
			$thumbHeight = $height;
		} else {
			$thumbHeight = ($imgHeight / $imgWidth) * $width;
		}
		
		$thumbWidth = floor($thumbWidth);
		$thumbHeight = floor($thumbHeight);
		$returnValue = null;
		if ($return == 'both') {
			$returnValue = array('width' => $thumbWidth, 'height' => $thumbHeight);
		} elseif ($return == 'width') {
			$returnValue = $thumbWidth;
		} elseif ($return == 'height') { 
			$returnValue = $thumbHeight;
		} else {
			throw new Exception($return . ' is not a valid parameter for return type for getThumbnailDimensions in Pata_File');
		}
		
		$this->thumbnailDimensions[$cacheName] = $returnValue;
		return $returnValue;
	}
	
	public function getThumbnailPng($width = 0, $height = 0)
	{
		if (!isset($this->thumbnailPng[$width.'x'.$height])) {
			$img = $this->getImage();
			$imgWidth = imagesx($img);
			$imgHeight = imagesy($img);
			$thumbTop = 0;
			$thumbLeft = 0;
			// If we have specified widths and heights, use them. Otherwise calculate them
			if ($width == 0 && $height == 0) {
				$thumbWidth = $imgWidth;
				$thumbHeight = $imgHeight;
			} else {
				$dimensions = $this->getThumbnailDimensions($width, $height);
				$thumbWidth = $dimensions['width'];
				$thumbHeight = $dimensions['height'];
			}
			// These default to not being changed
			$scaleWidth = $thumbWidth;
			$scaleHeight = $thumbHeight;

			if ($imgWidth > $imgHeight) {
				$scaleHeight = ($imgHeight / $imgWidth) * $thumbWidth;
				$thumbTop = ($thumbHeight - $scaleHeight) / 2;
			} elseif($imgWidth < $imgHeight) {
				$scaleWidth = ($imgWidth / $imgHeight) * $thumbHeight;
				$thumbLeft = ($thumbWidth - $scaleWidth) / 2;
			}
			
			$thumb = ImageCreateTrueColor($thumbWidth, $thumbHeight);
			// Fill the image with transparency
			imagesavealpha($thumb, true);
			$trans_colour = imagecolorallocatealpha($thumb, 0, 0, 0, 127);
			imagefill($thumb, 0, 0, $trans_colour);
	
			imagecopyresampled($thumb, $img, $thumbLeft, $thumbTop, 0, 0, $scaleWidth, $scaleHeight, $imgWidth, $imgHeight);
			imageinterlace($thumb, true);
			$this->thumbnailPng[$width.'x'.$height] = $thumb;
		}
		return $this->thumbnailPng[$width.'x'.$height];
	}
	
	function getType()
	{
		$types = $this->getFileTypeExtentions();
		foreach ($types as $type => $exts) {
			if (in_array(strtolower($this->getExtention()), $exts)) {
				return $type;
			}
		}
		return 'unknown';
	}
	
	function getFileTypeExtentions($type = null)
	{
		$types = array(
			'images'		=> array('jpg','jpeg','gif','png','tiff','bmp'),
			'documents'		=> array('doc', 'docx', 'txt', 'rtf', 'odt', 'pdf'),
			'spreadsheets'	=> array('xls'),
		);
		if ($type != null) {
			if (!isset($types[$type])) {
				throw new Exception('Pata_Gallery->getFileTypeExtentions() does not have a list of extentions for the "'.$type.'" type');
			}
			return $types[$type];
		}
		return $types;
	}
}
Tags:
Category: