Autoloading custom classes in Codeigniter

Edit: This is old. Install Composer instead, and require the vendor/autoload.php

I’ve been messing around with CodeIgniter recently. It’s not bad at all, and for non enterprise applications or small sites, it’s a good choice! However I wanted to bring across my own classes that I’d already wrote, so what we need to do is write a helper script to set up SPL Autoloading. All my classes were written using the conventions of Zend Framework, so rather than write my own autoload feature, I thought ‘Hmm, I could also use the validators, filters, form elements, and a couple of other awesome non MVC related stuff from ZF. ‘ So here’s how:

Create a file called zend_framework_helper.php

ini_set("include_path", ini_get("include_path").PATH_SEPARATOR.BASEPATH."../application/third_party/");
 require_once 'Zend/Loader/Autoloader.php';
 spl_autoload_register(array('Zend_Loader_Autoloader', 'autoload'));

Copy your Zend Folder to /application/third_party/ . Last thing you need to do is autoload the helper. Open config/autoload.php, find the helpers array, and add ‘zend_framework’ to it.

To get your own ZF_Style_Class to autoload, (my custom classes in this instance are namespaced DD), add the namespace to the Zend Autoloader. You can put this in its own helper or just put it after the spl_autoload_register command in the zf helper.

Zend_Loader_Autoloader::getInstance()->registerNamespace('DD_');

So now in my Codeigniter controllers, I can call my own classes, and that of Zend Framework! Remember to setView(new Zend View()) in your Form objects! Check it out:

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class Nofun extends MX_Controller {

 public function index()
 {
   $button = new Zend_Form_Element_Button('Frankenstein');
   $form = new Zend_Form('formname'); 
   $form->setView(new Zend_View()); 
   $form->addElement($button); 
   echo 'this rocks!<br />'.$form; 
   $test = new DD_Test(); 
   echo $test->pullLever(); 
 } 
}

Screen Shot 2013-05-23 at 21.12.50

Now we have the power of the non MVC Zend Stuff at out disposal!

If you’re interested in Codeigniter check out the awesome series of videos by David Connelly of DC Radio Network (www.dcradionetwork.com), introduction video below!

Installing a ZendFramework App on IIS

Yes yes I know. Please. I’m just doing my job.

Anyway, hatred of MS and their less than satisfactory web based products aside, heres how to set up a Zend Framework App on IIS. I am assuming IIS is already up and running and that PHP is installed (only because thats where I started from)

Open IIS, in the computer click on Web Sites.
In the action menu click new website.
Put in a description, hit next.
Stick the servers IP in, port, and domain, hit next
Browse to sites home path hit next
Assign permissions (I had to browse to folder, right click and un-read-only my session, uploads, and logs folders), hit next, and finish.

Ok. Now IIS uses some thing thats similar to .htaccess, only it isn’t. Its called IIRF, and it needs setting up if it isn’t already. You can get it here: Download the Ionic’s ISAP Rewrite Filter (IIRF) here

Create Iirf.ini and shove this in it: (this took hours of farting around to get working :-s )

RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d

RewriteRule ^/(.*(\.js|\.swf)(\?.+))$ - [L,I]
RewriteRule ^/(.*(\.js|\.ico|\.gif|\.jpg|\.png|\.css|\.html|\.htm|\.swf|\.flv))$ /$1 [L,I]
RewriteRule ^/(.*)$ /index.php/$1 [L,I]

Put this in your Zend Apps public folder. Note that the commented out rule is for ignoring css js and image files etc in your public folder so as not to get rewritten. However it messes with your GET parameters when you have a filename in the query.(IF anyone knows how to write a rule that say continue to route it if those extensions come after a question mark then PLEASE let me know!)

Restart IIS, and that should be you!

NB. Neither delboy1978uk nor anyone else endorses the use of IIS for any serious web development.

NB. I have found normal clean urls work but ?query=param gets dont! i will update once i fix this!

Zend Framework 2.0 released today!

I had a Zend Framework related question, so I went onto IRC to get help. IRC is the place to be! You” probably know Akrabat (Rob Allen). Well he was there too. And the chat was ‘congrats on the release!’

Lo and behold when I checked framework.zend.com, the entire site had changed! ZF2 is here! And I couldn’t have timed it more perfectly. My PHP is now at 5.4, and  I have Doctrine 2 up and running!

I suggest we waste absolutely no time, and download it then run through the tutorial! We have lots to learn! The new PHP namespaces, and dependency injection and a whole bunch more! Back soon with some juisy gossip lol!

Google QR Codes and Zend_PDF

Now that we can generate QR codes using the Google Chart API, and we have a controller action that serves it up for us with the correct .PNG header (see my previous post), we need to get it where it’s of any use – on paper!

So following on from my last post about Zend_Pdf, we can now do the following to get it in our PDF:

//draw qr code
 $target = 'http://example.co.uk/download/qr?target=http://www.example.co.uk/properties/view/id/'.$prop->getID();
 $img = file_get_contents($target); 
 file_put_contents(APPLICATION_PATH.'/uploads/tmp/'.$prop->getID().'-qr.png',$img);
 $image = Zend_Pdf_Image::imageWithPath(APPLICATION_PATH.'/uploads/tmp/'.$prop->getID().'-qr.png');
 $page->drawImage($image, 13, 13, 94,94);
 unlink(APPLICATION_PATH.'/uploads/tmp/'.$prop->getPid().'-qr.png');

The first line of code is the url of our download controllers QR action.  file_get_contents stores the image in memory, which we then save into a temp folder using file_put_contents. These commands are the equivalent of a lot of fopen etc, so use them if you don’t already!  This example uses my property object to make a dynamic filename, but this isn’t necessary, but I don’t need to tell you that!

We then use Zend_Pdf to load our png, and when drawing an image, the numbers in the argument are x1, y1, x2, y2 being bottom left and top right corners respectively. Finally, ditch the temp file with the unlink command. Have Fun!

Getting a Zend Framework site working with a standard PHP site

I have a standard php site in work, which suddenly was to be joined onto a zend site. Wtf. Anyway.

Create a file zend_init.php and require that in your header.

<?php
// Define path to application directory
defined('APPLICATION_PATH')
 || define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/../application/'));
 //die(APPLICATION_PATH);
// Define application environment
defined('APPLICATION_ENV')
 || define('APPLICATION_ENV', 'development');
 
// Ensure library/ is on include_path
set_include_path(implode(PATH_SEPARATOR, array(
 realpath(APPLICATION_PATH . '/../library'),
 get_include_path(),
)));
/** Zend_Application */
require_once 'Zend/Application.php';
// Create application, bootstrap, and run
$application = new Zend_Application(
 APPLICATION_ENV,
 APPLICATION_PATH . '/configs/application.ini'
);
$application->bootstrap();

That’s it! Just edit the path to find where your application folder is.

Now without having to do any require_once() or include() commands, we can simply just call a new object like you would in your mvc app!

Zend_PDF with templates

Zend_PDF rocks! It makes drawing a PDF almost child’s play. The main time eater though is the trial and error with the co-ordinates. So I stopped all this nonsense, and used Illustrator.

In adobe Illustrator, set the ruler origin to the bottom left of the page. Now in the transform pallette you can get the XY co-ordinates! Once you have created a mock up of the PDF, save a copy minus all the dynamic stuff, which we’ll drop in from our controller action.

First up, ditch the view and layout:

//disable the layout
 Zend_Layout::getMvcInstance()->disableLayout(); 
 $this->_helper->viewRenderer->setNoRender();

Next we create the Zend_PDF and templates, and set default fonts and colours:

//create a zend pdf
 $template = Zend_Pdf::load(APPLICATION_PATH.'/layouts/pdfs/proplist.pdf');
 $this->_pdf = new Zend_Pdf();
 $extractor = new Zend_Pdf_Resource_Extractor();
//set font
 $font = Zend_Pdf_Font::fontWithName(Zend_Pdf_Font::FONT_HELVETICA);
 $bold = Zend_Pdf_Font::fontWithName(Zend_Pdf_Font::FONT_HELVETICA_BOLD);
//set colors
 $black = new Zend_Pdf_Color_Html('#000000');
 $red = new Zend_Pdf_Color_Html('#ED1F24');
 $green = new Zend_Pdf_Color_Html('#009444');
 $blue = new Zend_Pdf_Color_Html('#0069B5');

We will have so many records per page, so the next bit goes in a for loop:

//add page
 $page = $extractor->clonePage($template->pages[0]);

 //get properties for this page
 $props = $this->_helper->paginator($rows,6);

We can set colours and fonts like this:

$page->setFillColor($black);
$page->setFont($bold, 11);

Add the page to the pdf like this

$pdf->pages[] = $page;

I created a function for getting the coordinates of each element, which adjusts depending which record on the page we are dealing with. This could be tweaked to suit your own project.

private function getCoordinates($row, $elem)
 {
 $xCoords = array(
 'header' => 21,
 'avail_date' => 419,
 'price' => 541,
 'summary' => 21,
 'hol_dep' => 70,
 'dep_date' => 128,
 'board_up' => 217,
 'keys' => 267,
 'tax_band' => 333,
 'on_web' => 388,
 'ref' => 437,
 'images' => 560,
 'condition' => 59,
 'move_date' => 130,
 'gas_safety' => 221 ,
 'hwsa' => 271 ,
 'let_fee' => 327,
 'man_fee' => 391,
 'landlord' => 452,
 'lha' => 551,
 'comment' => 64
 );

 $yCoords = array(
 'header' => 759,
 'avail_date' => 759,
 'price' => 759,
 'summary' => 739,
 'hol_dep' => 692,
 'dep_date' => 692,
 'board_up' => 692,
 'keys' => 692,
 'tax_band' => 692,
 'on_web' => 692,
 'ref' => 692,
 'images' => 692,
 'condition' => 680,
 'move_date' => 680,
 'gas_safety' => 680,
 'hwsa' => 680,
 'let_fee' => 680,
 'man_fee' => 680,
 'landlord' => 680,
 'lha' => 680,
 'comment' => 667
 );

 $z = array();
 $z['x'] = $xCoords[$elem];

 switch($row)
 {
 case 1:
 default:
 $z['y'] = $yCoords[$elem];
 break;
 case 2:
 $z['y'] = $yCoords[$elem] - 127.5;
 break;
 case 3:
 $z['y'] = $yCoords[$elem] - (127.5 * 2);
 break;
 case 4:
 $z['y'] = $yCoords[$elem] - (127.5 * 3);
 break;
 case 5:
 $z['y'] = $yCoords[$elem] - (127.5 * 4);
 break;
 case 6:
 $z['y'] = $yCoords[$elem] - (127.5 * 5);
 break;
 }
 return $z;
 }

Finally we can draw the text like this:

$text = '?';
 $page->setFillColor($blue);
 $z = $this->getCoordinates($row, 'condition');
 $page->drawText($text, $z['x'], $z['y'],'UTF-8');

Other things include fitting text to a specific width, or making a paragraph of multi lines:

private function widthForStringUsingFontSize($string, $font, $fontSize)
 {
 $drawingString = iconv('UTF-8', 'UTF-16BE//IGNORE', $string);
 //$drawingString = $string;
 $characters = array();
 for ($i = 0; $i < strlen($drawingString); $i++) {
 $characters[] = (ord($drawingString[$i++]) << 8 ) | ord($drawingString[$i]);
 }
 $glyphs = $font->glyphNumbersForCharacters($characters);
 $widths = $font->widthsForGlyphs($glyphs);
 $stringWidth = (array_sum($widths) / $font->getUnitsPerEm()) * $fontSize;
 return $stringWidth;
 }
private function getFontSizeForWidth($text,$font,$desiredSize,$maxWidth)
 {
 do 
 {
 $stringWidth = $this->widthForStringUsingFontSize($text, $font, $desiredSize);
 $desiredSize = $desiredSize - 0.5; 
 }
 while($stringWidth > $maxWidth);
 return $desiredSize + 0.5;
 }
private function getWrappedText($string, $font, $fontsize,$max_width)
 {
 $wrappedText = '' ;
 $lines = explode("\n",$string) ;
 foreach($lines as $line) {
 $words = explode(' ',$line) ;
 $word_count = count($words) ;
 $i = 0 ;
 $wrappedLine = '' ;
 while($i < $word_count)
 {
 /* if adding a new word isn't wider than $max_width,
 we add the word */
 if($this->widthForStringUsingFontSize($wrappedLine.' '.$words[$i]
 ,$font
 , $fontsize) < $max_width) {
 if(!empty($wrappedLine)) {
 $wrappedLine .= ' ' ;
 }
 $wrappedLine .= $words[$i] ;
 } else {
 $wrappedText .= $wrappedLine."\n" ;
 $wrappedLine = $words[$i] ;
 }
 $i++ ;
 }
 $wrappedText .= $wrappedLine."\n" ;
 }
 return $wrappedText ;
 }

To align text to the right, we subtract the width:

$width = $this->widthForStringUsingFontSize($text, $bold, 12);
 $z = $this->getCoordinates($row, 'price');
 $z['x'] = $z['x'] - $width;

And if we need wrapped text:

$text = $this->getWrappedText($text, $font,10, 550);

Awesome! Another what used to be pain in the arse task is now a piece of cake (or zf rather lol)!

Zend_Framework on cPanel

I was replacing an old website hosted on a dedicated CPanel Apache server, with a new kick ass site written in Zend Framework. The problem with CPanel is that they don’t like you farting around with the DocumentRoot of Apache (by default /home/user/public_html/), which for a ZF app would be /home/user/public_html/public.

The way around this, without editing your vhosts or your httpd.conf (people with no dedicated server won’t be allowed to edit these anyway), is to upload a level down and symlink your public folder.

I use Dreamweaver (but ONLY for it’s FTP client!! DW totally sucks!) for my uploads, so in your FTP client I changed the upload folder to / instead of /public_html. Upload your files, and then SSH into the server.

we need to symlink the public_html folder, so make sure you’re not root for this part.  From your home folder:

# rm -fr ./public_html
# su nonrootuser
$ ln -s public public_html
$ exit
# chown -R nonrootuser:nonrootuser /home/nonrootuser/public_html
# chmod -R 0755 /home/nonrootuser/public_html

I’m not sure why, but symlinking whilst I was root seemed to cause problems, hence why i changed user before issuing the command. The chown and chmod fix a 500 error that says
“SoftException in Application.cpp:431: Mismatch between target GID (503) and GID (99) of file “/home/nonrootuser/public_html/index.php”

There we have it! Your ZF app is now loading propertly!

Internationalisation with Zend_Framework

I’m loving discovering all these other parts of Zend Framework that I haven’t looked at yet. Today it was setting the locale of the application, and dishing out content specific to that region. Okay, lets get cracking.

In the root of the application, create /languages

And in there, create files like messages.es.php & messages.fr.php and messages.en_US.php. For English, as well as having en_GB and en_US, we should also have just en as a fallback. In these files, you put your translations in a big ass array:

<?php
return array(
  'nav-home'             => 'Inicio',
  'nav-services'         => 'Servicios',
  'nav-catalogue'         => 'Catálogo',
  'nav-contact'         => 'Contacto',
  'welcome'         => 'Bienvenido',
  'form-name'         => 'Nombre:',
  'form-email-address' => 'email:',
  'form-message'       => 'Mensaje:',
  'form-verification'  => 'verificación:',
  'form-send-message'  => 'Enviar',
  'form-title'         => 'Contacto',
);

Then you initialise the locale stuff in your bootstrap. If it can’t detect the locale from your browser then it will fall back to your default locale of choice :

protected function _initLocale()
{
    try 
    {
      $locale = new Zend_Locale('browser');
    } 
    catch (Zend_Locale_Exception $e) 
    {
      $locale = new Zend_Locale('en_GB');
    }
    $registry = Zend_Registry::getInstance();
    $registry->set('Zend_Locale', $locale);
}

To test the different locales, we can edit the bootstrap replacing the word browser for fr or es or en_US etc. But before we do that, a couple more things. First, initialise the translate stuff in the bootstrap too.

protected function _initTranslate()
{
    $translate = new Zend_Translate('array',
                  APPLICATION_PATH . '/../languages/',
                  null,
                  array('scan' => Zend_Translate::LOCALE_FILENAME,
                        'disableNotices' => 1));
    $registry = Zend_Registry::getInstance();
    $registry->set('Zend_Translate', $translate);
}

In the controller actions where you want anything to happen you get it from the Zend_Registry:

    $registry = Zend_Registry::getInstance();
    $this->view->locale = $registry->get('Zend_Locale');

Finally, you need to edit the nav links and the form field labels like so:

    <a href="blah"><?php echo $this->translate('nav-catalogue');?></a>

And the form:

    $email->setLabel('form-email-address');

Zend_Form is groovy enough to check for translations automatically, so that’s it really! A lot of fuss taken care of quite simply!

Another point is that now we have a locale, we can get locale specific versions of things like dates, currency, and number formatting. As an example:

echo $this->escape(
    Zend_Locale_Format::toNumber
    (
        $this->product->price,
        array
        ( 
            'locale' => $this->locale,
            'precision' => 2
        ) 
    )
);

And for a date:

    <?php echo $this->escape($this->productdate->get(Zend_Date::DATE_FULL)); ?>

As you can see this is easy and fun! So it would be awesome to have the ability to switch between them, yes? So lets make a locale controller:

class LocaleController extends Zend_Controller_Action

{

public function setAction()
 {
    // if supported locale, add to session
    if (Zend_Validate::is($this->getRequest()->getParam('locale'), 'InArray',
        array('haystack' => array('en_US', 'en_GB', 'de_DE', 'fr_FR')))) 
    {
      $session = new Zend_Session_Namespace('ttb.l10n');
      $session->locale = $this->getRequest()->getParam('locale');
    }
    // redirect to requesting URL
    $url = $this->getRequest()->getServer('HTTP_REFERER');
    $this->_redirect($url);
 } 
}

edit the _initLocale method in the bootstrap

  protected function _initLocale()
  {
     $session = new Zend_Session_Namespace('ttb.l10n');
     if ($session->locale) 
     {
        $locale = new Zend_Locale($session->locale);
     }
     if ($locale === null) 
     {
        try 
        {
            $locale = new Zend_Locale('browser');
        } 
        catch (Zend_Locale_Exception $e) 
        {
          $locale = new Zend_Locale('en_GB');
        }
     }
     $registry = Zend_Registry::getInstance();
     $registry->set('Zend_Locale', $locale);
  }

And then make some links in your layout to swap locales:

<a href="/locale/set/fr_FR"><img src="/img/locale/france-flag.png" />

Awesome! switching locales is a dawdle! Now to get on with my Spanish lessons some more! Adios!

Creating a Download Controller in Zend Framework

We’re playing around with a Zend Form Element Captcha Image and we need to keep the images generated somewhere. In the public folder? I’d rather not. The same applies for user uploads, and session files. However our browsers won’t be able to map an image from the url if its in the non public folders. So I’m setting up a Download Controller.

Ok so we need folders for all this stuff. In the shell at the root of the site:

mkdir uploads
mkdir session
mkdir captcha
chmod 777 uploads
chmod 777 session
chmod 777 captcha 
cd library
mkdir fonts
chmod 777 fonts

Next thing – let’s not hard code any paths. We create an ini file called
/application/configs/paths.ini
and type this out:

[paths]
uploads = /../uploads/
captcha = /../captcha/
fonts = /../library/fonts/
captchafont = Tahoma.ttf

And of course copy and paste the Tahoma.ttf file into /library/fonts.

If you haven’t created your own library, you should do it. Certain components you write will be useful in lots of sites. In this example, a standard contact form can be reused over and over again.
Create a folder in the library. Your classes will start in the Zend way; Zend_Form, Zend_Date, so choose your folder name carefully 😉 For my own nostalgic reasons of programming Atari ST’s when I was younger, my library is called TTB.
In your application.ini, add the following line:

autoloaderNamespaces[] = "TTB_"

replacing TTB with your folder name. Then put a forms folder in that and create a Contact.php

In the init section of the form (TTB_Form_Contact), we want to get the paths for the fonts for the captcha, so we create a $config variable:

//get the relevant path configuration
 $config = new Zend_Config_Ini( APPLICATION_PATH.'/configs/paths.ini', array('paths'));

And the captcha element looks like this:

$captcha = new Zend_Form_Element_Captcha('captcha', array(
    'captcha' => array(
        'captcha' => 'Image',
        'wordLen' => 6,
        'timeout' => 300,
        'width'   => 300,
        'height'  => 100,
        'imgUrl'  => '/captcha',
        'imgDir'  => APPLICATION_PATH . $config->captcha,
        'font'    => APPLICATION_PATH .$config->fonts.$config->captchafont
        )
    ));

our standard captcha URL is /captcha, so we need to set up a route. In your initRoutes in the bootstrap:

//route for download
$router->addRoute('download', new Zend_Controller_Router_Route('download/:file', array('controller' => 'download', 'action' => 'index')));
//route for captchas
$router->addRoute('captcha', new Zend_Controller_Router_Route('captcha/:file', array('controller' => 'download', 'action' => 'captcha')));

This sets up URL’s like /download/photo.jpg, or /captcha/jgf235jf4fuiy2gfc.png. They point to a download controller, so lets create one:

class DownloadController extends Zend_Controller_Action
{
    /** @var string $path the relevant folder */
    protected $_path;
    public function init()
    {
        //get the relevant path
         $this->config = new Zend_Config_Ini( APPLICATION_PATH.'/configs/paths.ini', array('paths'));
    }
    public function indexAction()
    {
        $this->_path = $this->config->uploads;
        $this->generate();
    }
    public function captchaAction()
    {
        $this->_path = $this->config->captcha;
        $this->generate();
    }
}

This sets the path to be used before rendering the image, which happens in $this->generate(), the meat and bones of the download controller:

private function generate()
{
    $file = $this->getRequest()->getParam('file');

    // disable view and layout 
    Zend_Layout::getMvcInstance()->disableLayout(); 
    $this->_helper->viewRenderer->setNoRender(); 

    // setup realpath to image 
    $path_to_image = APPLICATION_PATH.$this->_path.$file;  

    //set mimetype (content-type image/jpeg etc)
    $mtype = '';

    // magic_mime module installed?
    if (function_exists('mime_content_type')) 
    {
         $mtype = mime_content_type($path_to_image);
    }
    // fileinfo module installed?
    else if (function_exists('finfo_file')) 
    {
        $finfo = finfo_open(FILEINFO_MIME); // return mime type
        $mtype = finfo_file($finfo, $path_to_image);
        finfo_close($finfo); 
    } 
    // set headers 
    header("Content-Type: ".$mtype);

    // Open the file for reading
    $fh = fopen($path_to_image, 'r'); 

    // And pass it through to the browser
    fpassthru($fh);
}

Now you have an awesome captcha, a great way of configuring your paths, and a download controller which can serve up images from the protected areas of your site!