Category Archives: cakePHP 2.1

CakePHP is a rapid development framework for PHP that provides an extensible architecture for developing, maintaining, and deploying applications. Using commonly known design patterns like MVC and ORM within the convention over configuration paradigm, CakePHP reduces development costs and helps developers write less code.

How to validate image uploads in cakePHP 2?

The Validation class in CakePHP contains many validation rules that can make model data validation much easier. I need to validate an image resolution (min 300 dpi),  upload size (max 40 Mb) and formats like .jpg, .tif, .eps, .bmp, .png, .pdf, .psd, .ai, .ps or .gif.
The list of common MIME types and their corresponding file extensions are available via sites like reference.sitepoint.com/html/mime-types-full or www.webmaster-toolkit.com/mime-types.shtml.

App::uses('AppModel', 'Model');

class Image extends AppModel {
//array of accepted Mime types
private $allowedMime = array(
    'application/postscript'//EPS, AI, PS, EPT
    ,'image/bmp','image/x-windows-bmp' 	//BMP
    ,'image/gif'                            //GIFF
    ,'image/jpeg','image/pjpeg' 		//GPEG
    ,'application/pdf'			//PDF
    ,'image/png','image/x-png'		//PNG
    ,'application/octet-stream' 		//PSD
    ,'image/tiff','image/x-tiff','image/tiff','image/x-tiff'//TIF

);
private $maxSize = 40960;
private $imgProperties;
private $img;
//format used to validate file extension
public $verify = array(
    'format' => array('AI','BMP','EPS','GIF','JPEG','PDF','PNG','PS','PSD','TIFF','EPT'),
    'min_width_inches' => 4,
    'min_height_inches' => 4,
    'min_dpi'=>300, //PixelsPerInch
    'min_dpc'=>118 //PixelsPerCentimeter
);
public $uploadIds=array();
private $path;
public $Errors=array();
public $responce=array();

//function _getImageMimeType return back the image properties before save file to ftp and information to database
function beforeSave() {
$this->data['Image']+=array('format'=>$this->imgProperties['format']
   ,'dimensions_width_inches' =>number_format($this->imgProperties['geometry']['width_inches'])
   ,'dimensions_height_inches' =>number_format($this->imgProperties['geometry']['height_inches'])
   ,'dimensions_width_pixels' =>$this->imgProperties['geometry']['width']
   ,'dimensions_height_pixels' =>$this->imgProperties['geometry']['height']
   ,'resolution_width_dpi'=>$this->imgProperties['resolution']['x']
   ,'resolution_height_dpi'=>$this->imgProperties['resolution']['y']
   ,'colourspace'=>$this->imgProperties['colorSpace']
   ,'size'=>$this->data['Image']['file']['size']
   ,'path'=>$this->path
);
unset($this->data['Image']['file']);

return true;
}

public $validate = array(
   'file' => array(
   'checkupload' =>array(
   'rule' => array('checkUpload', false),
   'message' => 'No file was uploaded',//'Invalid file',
   'on' => 'create'
   ),
   'checkuploadedit' =>array(
   'rule' => array('checkUpload', false),
   'message' => 'No file was uploaded',//'Invalid file',
   'on' => 'update'
   ),
   'checksize' => array(
   'rule' => array('checkSize',false),
   'message' => 'Invalid File size',
   'on' => 'create'
   ),
   'checksizeedit' => array(
   'rule' => array('checkSize',false),
   'message' => 'Invalid File size',
   'on' => 'update'
   ),
   'checktype' =>array(
   'rule' => array('checkType',false),
   'message' => 'Invalid File type. Accepted file types: .jpg, .tif, .gif, .png, .psd, .pdf, .eps, .ai',
   'on' => 'create'
   ),
   'checktypeedit' =>array(
   'rule' => array('checkType',false),
   'message' => 'Invalid File type. Accepted file types: .jpg, .tif, .gif, .png, .psd, .pdf, .eps, .ai',
   'on' => 'update'
   ),
   'checkdimensions' =>array(
   'rule' => array('checkDimensions',false),
   'message' => 'Uploaded file does not have a min dimensions: 2.0 x 1.0 inches @ 300 x 300 DPI',
   'on' => 'create'
   ),
   'checkdimensionsedit' =>array(
   'rule' => array('checkDimensions',false),
   'message' => 'Uploaded file does not have a min dimensions: 2.0 x 1.0 inches @ 300 x 300 DPI',
   'on' => 'update'
   ),
   'checkuploadedfile' =>array(
   'rule' => array('checkUploadedFile',false),
   'message' => 'No file was uploaded',
   'on' => 'create'
   ),
   'checkuploadedfileedit' =>array(
   'rule' => array('checkUploadedFile',false),
   'message' => 'No file was uploaded',
   'on' => 'update'
   ),
),
);

//The Associations below have been created with all possible keys, those that are not needed can be removed
/**
* Custom validation rule for uploaded files.
*
* @param Array $data CakePHP File info.
* @param Boolean $required
* @return Boolean
*/
function checkUpload($data, $required = false){$data = $data["file"];

// Remove first level of Array ($data['Image']['size'] becomes $data['size']) $data = array_shift($this->data["Image"]["file"]);
if(!$required && $data['error'] == 4){$this->Errors["checkUpload"]['error']=$data['error'];
CakeLog::write("imageLogs", __METHOD__." No file was uploaded: ".Debugger::exportVar($data,$recursion=1));
return false;}
// Check for Basic PHP file errors.
if($required && $data['error'] !== 0){$this->Errors["checkUpload"]['error']=$data['error'];
CakeLog::write("imageLogs", __METHOD__." Failed to upload file: ".Debugger::exportVar($data,$recursion=1));
return false;}
// No file uploaded.
if($required && $data['size'] == 0){$this->Errors["checkUpload"]['error']=$data['size'];
CakeLog::write("imageLogs", __METHOD__." No file was uploaded: ".Debugger::exportVar($data,$recursion=1));
return false;}

// Catch I/O Errors.
if ($required && !is_readable($data['tmp_name'])){$this->Errors["checkUpload"]['tmp_name']=1;
CakeLog::write("imageLogs",__METHOD__." Failed to write file to disk: {$data['tmp_name']}");
return false;
}
// Finally, use PHP’s own file validation method.
if ($required && !is_uploaded_file($data['tmp_name'])){$this->Errors["checkUpload"]['tmp_name']=2;
CakeLog::write("imageLogs", __METHOD__." Missing a temporary folder: ".Debugger::exportVar($data,$recursion=1));
return false;}
return true;
}

function checkSize($data, $required = false){$data = $data["file"];
if(!$required && $data['error'] == 4){
$this->Errors["checkSize"]['error']=$data['error'];
CakeLog::write("imageLogs", __METHOD__." No file was uploaded: ".Debugger::exportVar($data,$recursion=1));
return false;}
if($data['size'] == 0||$data['size']/1024 > $this->maxSize){
$this->Errors["checkSize"]['error']=$data['size']/1024;
CakeLog::write("imageLogs", __METHOD__." The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form: ".($data['size']/1024).' '.Debugger::exportVar($data,$recursion=1));
return false;}
return true;
}

/**
* Use LibGD to determine if an uploaded file is a valid Image by
* running it's mime-type against a list of $valid_mime_types
*
* @param Array $data CakePHP File info
* @return Boolean
*/
function checkType($data, $required = false,$allowedMime = null){$data = $data["file"];
//File upload stopped by extension
if(!$required && $data['error'] == 4){
$this->Errors["checkType"]['error']=$data['error'];
CakeLog::write("imageLogs", __METHOD__." No file was uploaded: ".Debugger::exportVar($data,$recursion=1));
return false;}

// Check the MimeType against the array of valid ones specified above
if(!in_array((strtolower($data['type'])), $allowedMime=$this->allowedMime)){
$this->Errors["checkType"]['type']=$data['type'];
CakeLog::write("imageLogs", __METHOD__." Uploaded image has rejected Mime Type: ".$data['type'].' '.Debugger::exportVar($this->data["Image"],$recursion=1));
return false;}

// Retrieve the MimeType of Image, if none is returned, it's invalid
if (!$mime_type = $this->_getImageMimeType($data)){
$this->Errors["checkType"]['type']=$mime_type;
CakeLog::write("imageLogs",__METHOD__." Uploaded file does not have a mime-type ".Debugger::exportVar($data,$recursion=1));
return false;
}

// Check the MimeType against the array of valid ones specified above
//if(empty($allowedMime)){$allowedMime=$this->allowedMime;}
if(!in_array((strtoupper($mime_type)), $this->verify['format'])){
$this->Errors["checkType"]['type']=$mime_type;//$this->Errors["checkType"]['mime_type']=$mime_type;
CakeLog::write("imageLogs", __METHOD__." Uploaded image has rejected Mime Type: ".$data['type'].' '.Debugger::exportVar($this->data["Image"],$recursion=1));
return false;}

return true;
}

/**
* Use LibGD to return an uploaded Image's MimeType as a String, FALSE
* on errors or if the file is not an image
*
* @param String $filename Absolute path to file on disc
* @return String Image MimeType of $filename, false on failure
*/
function _getImageMimeType($data){

// If this error is thrown LibGD is not installed on your server.
if (!function_exists('getimagesize')){debug(__METHOD__." LibGD PHP Extension was not found, please refer to http://www.php.net/manual/en/book.image.php"); exit();}
try {
$image = new Imagick($data["tmp_name"]);
$imgProperties=$image->identifyImage(); //Imagick::getImageResolution()
//CakeLog::write("imageLogs",__METHOD__.Debugger::exportVar($imgProperties,$recursion=1) );
$imgProperties['format']=strtok($imgProperties['format'], ' ');
$imgProperties['geometry']['width_inches'] = $imgProperties['geometry']['width']/$imgProperties['resolution']['x'];
$imgProperties['geometry']['height_inches']= $imgProperties['geometry']['height']/$imgProperties['resolution']['y'];

} catch (Exception $e) {//throw new LogoReadErrorException();
$this->Errors["checkType"]['type']=$imgProperties['format'];
CakeLog::write("imageLogs",__METHOD__." Uploaded file does not have a mime-type ".Debugger::exportVar($e).' '.Debugger::exportVar($data,$recursion=1));
}

if (isset($imgProperties['format'])){
$this->imgProperties=$imgProperties;
return $imgProperties['format'];}
return false;
}
/*
* Accepted min dimensions: 2.0 x 1.0 inches @ 300 x 300 DPI
* */
function checkDimensions() {$flag=1;
if(isset($this->imgProperties["units"]))
switch($this->imgProperties["units"]){
case "PixelsPerInch":
$flag=(((int)($this->imgProperties['geometry']['width_inches']) >= (int)($this->verify['min_width_inches']))
&& ((int)($this->imgProperties['geometry']['height_inches']) >= (int)($this->verify['min_height_inches']))
&& ((int)($this->imgProperties['resolution']['x']) >= (int)($this->verify['min_dpi']))
)?1:0;
break;
case "PixelsPerCentimeter": ///CakeLog::write("imageLogs",__METHOD__.' PixelsPerCentimeter '.$flag);
$this->imgProperties['geometry']['width_inches']=$this->imgProperties['geometry']['width']/($this->imgProperties['resolution']['x']*2.54);
$this->imgProperties['geometry']['height_inches']=$this->imgProperties['geometry']['height']/($this->imgProperties['resolution']['y']*2.54);;
$flag=(((int)($this->imgProperties['geometry']['width_inches']) >= (int)($this->verify['min_width_inches']))
&& ((int)($this->imgProperties['geometry']['height_inches']) >= (int)($this->verify['min_height_inches']))
&& ((int)($this->imgProperties['resolution']['x']) >= (int)($this->verify['min_dpc']))
)?1:0;
break;
case "Undefined":
switch($this->imgProperties["type"]){
case 'ColorSeparation':
$flag=(((int)($this->imgProperties['geometry']['width_inches']) >= (int)($this->verify['min_width_inches']))
&& ((int)($this->imgProperties['geometry']['height_inches']) >= (int)($this->verify['min_height_inches']))
)?1:0;
break;
case 'PaletteMatte':
$flag=(( (int)$this->imgProperties['geometry']['width'] > 1200)
&& ( (int)$this->imgProperties['geometry']['height'] > 1800)
)?1:0;
break;
}

break;
}
if($flag==0){
$this->Errors["checkDimensions"]['source']=$this->imgProperties;
$this->Errors["checkDimensions"]['required']=$this->verify;
CakeLog::write("imageLogs",__METHOD__." Uploaded file does not have a min dimensions: 4.0x6.0 inches @300x300DPI"
.Debugger::exportVar($this->imgProperties,$recursion=1).' '
.Debugger::exportVar($this->data["Image"],$recursion=1));
return false;
}
CakeLog::write("imageLogsUploadedOK",__METHOD__.Debugger::exportVar($this->imgProperties,$recursion=1)
.Debugger::exportVar($this->data["Image"],$recursion=1));
return true;
}

function checkUploadedFile($data, $required = false){
   $data = $data["file"];
   $this->path=$directory = WWW_ROOT.'img'.DS.'content'.DS.$this->data["Image"]["entries_id"];
   if(!is_dir($directory)) mkdir($directory);

   // Catch I/O Errors.
   if ($required && !is_readable($directory)){
      $this->Errors["checkUploadedFile"]['directory']="Failed to write file to disk: {$directory}";
      CakeLog::write("imageLogs",__METHOD__." Failed to write file to disk: {$directory}".' '.Debugger::exportVar($data,$recursion=1));
      return false;
}

   $this->data['Image']['name']=$this->data['Image']['priority']."_".$data['name'];
   if(!move_uploaded_file($data['tmp_name'], $directory.DS.$this->data['Image']['name'])){
      $this->Errors["checkUploadedFile"]['directory']=" Failed to move uploaded file: from {$data['tmp_name']}";
      CakeLog::write("imageLogs",__METHOD__." Failed to move uploaded file: from {$data['tmp_name']} to    ".$directory.DS.$data['name'].' '.Debugger::exportVar($data,$recursion=1));
      return false;
   }
return true;
}

}

How to share CakePHP 2.1 core for multiple apps?

CakePHP 2.1  allows you to use one set of core files while maintaining multiple applications.
Make sure the host is pointing to the correct directory like /var/www/Sites/app/webroot/. Verify mod_rewrite is in fact on. Enable mod_rewrite with: a2enmod rewrite.
Set up the .htaccess files in the /var/www/Sites/app/ and the /var/www/Sites/app/webroot directories.
The directory /var/www/frameworks/cakephp2.1 contains the core.

1. Change the following constants in /var/www/Sites/index.php:

if (!defined('CAKE_CORE_INCLUDE_PATH')) {
 //OLD: define('CAKE_CORE_INCLUDE_PATH', ROOT . DS . 'lib');
   define('CAKE_CORE_INCLUDE_PATH', DS.'var'.DS.'www'.DS.'frameworks'.DS.'cakephp2.1'.DS.'lib');
	}

2. Do changes in /var/www/Sites/app/webroot/index.php:

if (!defined('CAKE_CORE_INCLUDE_PATH')) {
  if (function_exists('ini_set')) {
 //ini_set('include_path', ROOT . DS . 'lib' . PATH_SEPARATOR . ini_get('include_path'));
 ini_set('include_path', DS.'var'.DS.'www'.DS.'frameworks'.DS.'cakephp2.0.6'.DS.'lib'. PATH_SEPARATOR . ini_get('include_path'));
		}
.....

As you can find same goes for plugins and vendors folder.
You can have only one cake core but you must have one app folder (containing MVC) by site.

Happy Coding!