jump to navigation

Blog tutorial Chapter 3 done June 24, 2006

Posted by rossoft in CakePHP.
354 comments

Continuation of the extension blog tutorial.

Chapter 3: Ajax and Cookies. Saving a draft of the editing post in cookies through transparent periodicall ajax calls with feedback effects to the user.

Now you can get a .tgz file that containsthe tutorial in .PDF and .ODT formats, and the complete application before the chapter 1, before the chapter 3 and after the chapter 3. You can’t get stuck anywhere.

Download directly PDF version or download the complete .tgz with source code.

Working on a extension of the blog tutorial June 23, 2006

Posted by rossoft in CakePHP.
40 comments

I’m building a ampliation of the blog tutorial of the manual. It starts between the manual tutorial and the 2nd tutorial in the wiki.
It will do a walk through some cool things in Cake. It has a lot of screenshots.

This is the first two chapters, cool flash messages + some error handling.

They’re a recopilation of useful blog posts on the well-known CakePHP blogs.

Download directly PDF version or download the complete .tgz with source code.

CakePhp Cheat Sheet June 23, 2006

Posted by rossoft in CakePHP.
15 comments

Download the nate's cheatsheet of CakePhp in pdf format HERE

Custom HTML Tags (WidgetHelper) June 20, 2006

Posted by rossoft in CakePHP.
17 comments

Do you want create custom helpers accessible in your views like:

Example of HighlightWidget:
<h:span class="high" search="/I'm \w+/">
          Hi, I'm John. What's your name? I'm Marcus.
</h:span>

And it will output the html:
Hi, <span class="high">I'm john</span>. What's your name?

Example of RepeaterWidget:
<?php $array=array('Frodo'=>'Elijah Wood','Aragorn'=>'Viggo Mortensen')
      $repeaterWidget->bind('cast',$array))?>
  <repeater:repeater array='cast'>
      <b><repeater:key/>:</b> <repeater:value/>
      <br/>
  </repeater>

 
Will output the html:
     <b>Frodo:</b>: Elijah Wood
     <br/>
     <b>Aragorn:</b>: Viggo Mortensen
     <br/>

The usage is really easy. The configuration is the same as a normal helper,
only include in your controller $helpers=array('xxx',…,'RepeaterWidget','HighlightWidget');
And the definition of new Widgets is easy too, see the Highlight & Repeater code:
HighlightWidgetHelper:

<?php
/**
 * HighlightWidget Helper
 *
 * Highlights some expression found in a html text.
 *
 * Example:
 * In style declaration:
 *         .high { color: yellow; }
 * In view:
 * <h:span class="high" search="/I'm \w+/">
 *         Hi, I'm john. What's your name?
 * </h:h>
 *
 * Will output the html:
 * Hi, <span class="high">I'm john</span>. What's your name?
 *
 * @author RosSoft
 * @version 0.1
 * @license MIT
 *
 */
require_once(dirname(__FILE__) . DS . 'widget_helper.php');
class HighlightWidgetHelper extends WidgetHelper
{
    //main tags name => array subtags
    var $tag=array('h:div','h:span');
    
    function tag_h_div($attr,$inner_html)
    {
        return $this->replace($attr,$inner_html,'div');
    }        
    
    function tag_h_span($attr,$inner_html)
    {
        return $this->replace($attr,$inner_html,'span');
    }
    
    function replace($attr,$inner_html,$type)
    {
        $class=@$attr['class'];
        $expression=@$attr['search'];
        return preg_replace($expression,"<$type class=\"$class\">" . '\' . "</$type>",$inner_html);        
    }
    
}
?>

RepeaterWidgetHelper:

<?php
/**
 * RepeaterWidget Helper
 *
 * Repeats the html within the tag <repeater:repeater>HTML</repeater:repeater>
 * that contains the subtags <repeater:key /> and <repeater:value />
 * It replaces the subtags with the key & value from an associative array.
 * You must bind the widget to the array through
 * Example:
 * <?php
 * $array=array('Frodo'=>'Elijah Wood','Aragorn'=>'Viggo Mortensen')
 * $repeaterWidget->bind('cast',$array))?> *
 * <repeater:repeater array='cast'>
 *     <b><repeater:key/>:</b> <repeater:value/>
 *     <br/>
 * </repeater>
 *
 * Will output the html:
 *     <b>Frodo:</b>: Elijah Wood
 *     <br/>
 *     <b>Aragorn:</b>: Viggo Mortensen
 *     <br/>
 *
 * @author RosSoft
 * @version 0.1
 * @license MIT
 *
 * @link http://www.phpit.net/article/create-html-widgets-php/4/
 */
require_once(dirname(__FILE__) . DS . 'widget_helper.php');
class RepeaterWidgetHelper extends WidgetHelper
{
    //main tags name => array subtags
    var $tag=array(
                'repeater:repeater'=>array('repeater:key','repeater:value')
            );
    
    function tag_repeater_repeater($attr,$inner_html)
    {
        $array=$this->_get_bound($attr['array']);        

        // loop through bound array
        $new_html = '';
        foreach ($array as $k=> $v)
        {
            $new_html .= $this->_process_subtags(array('key'=>$k,'value'=>$v));
        }
        return $new_html;
    }
    
    function subtag_repeater_key($attr,$inner_html,$params)
    {
        return $params['key'];
    }
    
    function subtag_repeater_value($attr,$inner_html,$params)
    {
        return $params['value'];
    }
}
?>

The widgets are helpers, then you can use others helpers with $helpers variable, also
you can call a function of them with $highlightWidget->somefunction() like before.

The core class is WidgetHelper. That class must be extended in your widgets (you must do
a include_once for this).

Put in app/views/helpers/widget_helper.php (it ends in _helper) :

<?php
/**
 * Widget Helper
 *
 * Parses custom html tags like
 * <mytag:mytag1 attr1="value1" attr2="value2">some html</mytag:mytag1>
 * (This class must be extended)
 *
 * @author RosSoft
 * @version 0.1
 * @license MIT
 *
 * The filename is widget_helper.php because you must
 * overload it, not use directly.
 *
 */
 
class WidgetHelper extends Helper
{
    var $_library; //static array of widgets instances
    
    //main tag names => array subtags
    var $tag=array();
    var $_data=array();
    
    var $helpers=array('Util');
    
    function __construct()
    {
        static $library=array();  //for php4 compat
           $this->_library=& $library;           
           
           $this->_library[]=& $this; //add current instance
        
        if (!in_array('Util',$this->helpers))
        {
            array_push($this->helpers,'Util');
        }
    }
    
    /**
     * afterRender Callback
     */
    function afterRender()
    {
        static $once=true;
        if (! $once) return;
        $once=false;
        
        $html=@ob_get_clean();
        ob_start();
        $matches=array();
        
        preg_match_all("/(<(\w+:\w+)([^>]*)>(.*?)<\/\\2>)|(<(\w+:\w+)([^>]*)\/>())/is", $html, $matches,PREG_SET_ORDER);
        foreach ($matches as $match)
        {
            if (isset($match[6]))
            {
                $tagname=$match[6];
                $attributes=$match[7];
                $inner_html=$match[8];
            }
            else
            {
                $tagname=$match[2];
                $attributes=$match[3];
                $inner_html=$match[4];                
            }
            $outer_html=$match[0];
            foreach ($this->_library as $widget)
            {
                foreach ($widget->tag as $tag=>$subtags)
                {
                    if (is_numeric($tag))
                    {
                        $tag=$subtags;
                        $subtags=array();
                    }
                    if ($tag==$tagname)
                    {
                        $html=$widget->_process_tag($html, $tag, $outer_html,$inner_html,$attributes,$subtags);
                    }
                }
            }
        }
        echo $html;
    }

    /**
     * Bind data to the widget
     *
     * @param string $name Name of the data
     * @param mixed $data The data
     */    
    function bind($name, $data)
    {
        $this->_data[$name] = $data;
    }

    /**
     * Process one tag
     * @param string $content The complete document
     * @param string $tag_html One tag
     */
    function _process_tag($html, $tag_id,$outer_html,$inner_html,$attributes,$subtags)
    {
        // Parse tag
        $attributes= $this->_parse_attributes($attributes);        

        $this->_tag_id=$tag_id;
        $this->_tag_inner_html=$inner_html;
        $this->_tag_subtags=$subtags;

        $method_name='tag_' . str_replace(':','_',$tag_id);
        $out=$this->$method_name($attributes,$inner_html);
        if ($out!==false)
        {
            return str_replace($outer_html,$out, $html);
        }
        else
        {
            //show the error through html entities
            return str_replace($outer_html,h($outer_html), $html);
        }
    }
    
    function _get_bound($name)
    {
        return $this->_data[$name];
    }
    
    function _get_field_value($fieldName)
    {
        return $this->Util->retrieve_value($fieldName);    
    }
    
    function _get_view_value($var_name)
    {
        return $this->view->_viewVars[$var_name];
    }

    function _process_subtags($params=array(),$html=null)
    {
        if ($html===null)
        {
            $html=$this->_tag_inner_html;
        }
        foreach ($this->_tag_subtags as $subtag)
        {
            preg_match_all("/(\<$subtag([^>]*)\>(.*?)\<\/$subtag\>)|(\<$subtag([^>]*)\/\>())/is", $html, $matches,PREG_SET_ORDER);
            for ($i=0; $i < count($matches); $i++)            
            {
                $outer_html= $matches[$i][0];
                if (isset($matches[$i][5]))
                {
                    $attributes=$matches[$i][5];
                }
                else
                {
                    $attributes=$matches[$i][2];
                }
                $attributes=$this->_parse_attributes($attributes);                                
                
                $inner_html= $matches[$i][3];
                $method_name='subtag_' . str_replace(':','_',$subtag);
                $out=$this->$method_name($attributes,$inner_html,$params);
                if ($out!==false)
                {                                
                    $html= str_replace($outer_html,  $out, $html);
                }
                else
                {
                    //show the error through html entities
                    $html= str_replace($outer_html,  h($out), $html);
                }                
            }
                                    
        }
        return $html;
    }
    
    function _parse_attributes($html)
    {
        $matches=array();
        $attributes=array();
        preg_match_all('/(\w+)\s*=\s*"([^"]*)"/is', $html, $matches,PREG_SET_ORDER);
        foreach ($matches as $attr)
        {
            $attributes[low($attr[1])]=$attr[2];
        }
        return $attributes;
    }          
                      
}
?>

Register Global Callback functions (updated) June 18, 2006

Posted by rossoft in CakePHP.
8 comments

That's an idea after reading this post. The helpers / components / etc. can have global callback functions, that functions are registered in another component / helper / vendor class.

For Example: in my extension to CakeLog class, you can do:

//Callback extension
vendor('callbacks/callbacks');
$callbacks=& Callbacks::get_instance();
$ret=$callbacks->execute('CakeLog',array('that is a param',55);
if ($ret) $output.=join("\n",$ret);

It executes all the callbacks registered with the name 'CakeLog' with 2 parameter values: a string and a number.

Then in your beforeFilter of your AppController, you can do:

vendor('callbacks/callbacks');
$callbacks=& Callbacks::get_instance();

$code='return "some code here";';
$callbacks->register_anonymous('CakeLog','$param1,$param2',$code);

$code='return "Your params: $param1 – $param2";';
$callbacks->register_anonymous('CakeLog','$param1,$param2',$code);

Then when you log something with $this->log('something'), the result will be:

18-06-06 10:18:51 [ /app/controllers/test_controller.php:36 TestController->myaction() ] Error: something
some code here
Your params: that is a param – 55

Also you can  use $callbacks->register_method('CakeLog',$this,'beforeLog');

and create the method beforeLog() in the controller for be called before logging. 

Copy to vendors/callbacks/callbacks.php

<?php

/**
 * Callbacks Class
 * @author RosSoft
 * @version 0.2
 * @license MIT
 */

class Callbacks
{
    var $_lib=array();
   
    /**
     * Singleton instance
     * @return object Callbacks
     */
    function & get_instance()
    {
        static $instance=array(); //must be an array for doing singleton
        if (! $instance)
        {
            $instance[0]=& new Callbacks();
        }   
        return $instance[0];
    }
    /**
     * Register an anonymous callback function (function dynamically created)
     *
     * @param string $name Callback name
     * (you can register more than one callback with same name, they will be executed in register order)
     * @param string $params Parameters names like '$name,$value' (always single quotes!)
     * @param string $code PHP Code of the function
     *
     * Example:
     * register_anonymous('CakeLog','$a,$b','return "Value of a: $a – Value of b: $b" )
     */
    function register_anonymous($name,$params,$code)
    {
        $this->_register($name,create_function($params,$code));
    }   
   
    /**
     * Register an object method callback
     *
     * @param string $name Callback name
     * @param object $object Instance of the class object
     * @param string $method Name of method of the object
     *
     * Example:
     * register_method('CakeLog',$this,'test') : will register $this->test
     */
    function register_method($name,&$object,$method)
    {
        $this->_register($name,array($object,$method));       
    }   
   
    /**
     * Execute a callback name
     * @param string $name Callback name
     * @param array $params Array of parameter values like array('john',606)
     * @return array. Array of returned values by all the callbacks with the given name. If null, then no callback registered
     */
    function execute($name,$params=array())
    {
        if (isset($this->_lib[$name]))
        {
            $ret=array();
            foreach ($this->_lib[$name] as $callback)
            {
                $ret[]=call_user_func_array($callback,$params);   
            }
            return $ret;                       
        }
        else
        {
            return null;
        }
    }
   
    /**
     * Registers a function
     * @param string $name Callback name
     * @param mixed $function Callback function
     */
    function _register($name,$function)
    {
        if (!isset($this->_lib[$name]))
        {
            $this->_lib[$name]=array();
        }
        $this->_lib[$name][]=& $function;       
    }

   
}

?>

Http Cache Component (updated) June 16, 2006

Posted by rossoft in CakePHP.
3 comments

Uses the HTTP 1.1 caching.
Example:

You've the 'modified' date from a record in your DB, then you must convert it to timestamp. In your action you can do
$this->httpCache->cache($modified_timestamp);
From now, the browser will cache that response, in the next request the browser will send that timestamp, and it will be compared with server's one. If the browser's one is not older than $modified_timestamp, then a status code of not modified is sent and the action exits.

Other example: expiration date
$this->httpCache->expires('+1 Day'); 
Then the page will be cached 1 day. You must use only one of those in every different url.

Copy to /app/controllers/components/http_cache.php
<?php
/**
 * Http Cache Component
 * Uses built in HTTP 1.1 caching mechanism
 * (304 Not Modified)
 * 
 * @author RosSoft
 * @license MIT
 * @version 0.2
 *
 * @link http://blog.codahale.com/2006/05/23/rails-plugin-http_caching/
 *
 *
 * Example 1:
 * $this->httpCache->cache($timestamp_modified_record);
 * $timestamp_modified_record is the timestamp of the content (like 'modified' field)
 *
 * Example 2:
 * $this->httpCache->expires('+1 Day');
 * The page will be cached until expire time.
 *
 * (cache and expires are incompatible, use one of them for each different url)
 */

class HttpCacheComponent extends Object
{
    var $enabled=true;
   
    /**
     * If the browser cache has a timestamp newer than the
     * modification date of the content, then a response of
     * not-modified is sent (and exits).
     *
     * @param integer $timestamp The modification date of the content (Unix timestamp)
     * @param boolean $no_cache If true, then it stores cache to user always
     *
     */   
    function cache($timestamp,$no_cache=false)
    {       
        if ($this->enabled)
        {
            $this->_cache_control();           
            if (env('HTTP_IF_MODIFIED_SINCE')
            && $timestamp <= strtotime(env('HTTP_IF_MODIFIED_SINCE'))
            && ! $no_cache)
            {
                header('Last-Modified: '.$this->_timestamp_to_gmt, true, 304);               
                exit();
            }
            else
            {
                header('Last-Modified: '.$this->_timestamp_to_gmt($timestamp), true, 200);
            }
        }
    }

    /**
     * Sets an expiration time for the page.
     *
     * @param mixed $expires A strtotime string / timestamp integer: expiration date.
     * @param boolean $no_cache If true, then it stores cache to user always
     */     
    function expires($expires='+1 Day',$no_cache=false)
    {
        if ($this->enabled)
        {
            $this->_cache_control();
            if (env('HTTP_IF_MODIFIED_SINCE')
            && time()<= strtotime(env('HTTP_IF_MODIFIED_SINCE'))
            && ! $no_cache)
            {
                header('Last-Modified: '.env('HTTP_IF_MODIFIED_SINCE'), true, 304);
                header("HTTP/1.1 304 Not changed");
                exit();
            }   
            else
            {
                header('Last-Modified: '.$this->_timestamp_to_gmt($expires), true, 200);
            }
        }                   
    }

    /**
     * Timestamp to GMT string   
     *
     * @param mixed $timestamp . A strtotime string / timestamp integer
     * @return string GMT time
     */
    function _timestamp_to_gmt($timestamp)
    {
        if (! is_numeric($timestamp))
        {
            $timestamp=strtotime($timestamp);                       
        }       
        return gmdate('D, d M Y H:i:s', $timestamp).' GMT';
    }   

    /**
     * Sets header for cache control
     */   
    function _cache_control()
    {
        header("Cache-Control: Public",true);
        header("Pragma: Public",true);   
    }   
}
?>

Latex Helper June 14, 2006

Posted by rossoft in CakePHP.
add a comment

Helper for rendering LaTeX formulas easy

Usage: in your view,

echo $latex->render(‘\displaystyle\int_{0}^{1}\frac{x^{4}\left(1-x\right)^{4}}{1+x^{2}}dx=
\frac{25}{8}-\pi’);

It will create an IMG tag to the destination URL of the image. You need a folder in webroot writable by the web sever

1. Download latexrender, and save class.latexrender.php to vendors/latexrender/

2. Edit class.latexrender.php and change the paths to your apps imagemagik and latex

3. Copy this app/views/helpers/latex.php and change the paths of your temp folder and the destination folder of images (writable by webserver under wwwroot)

<?php
/**
 * Latex Helper
 * Renders some LaTeX formula to a image
 * and returns the <IMG> tag to it
 *
 * You need to set the destination directory under webroot folder
 * to store the images (set write permissions on it)
 *
 * @author RosSoft
 * @version 0.1
 * @license MIT
 *
 * @link http://www.mayer.dial.pipex.com/tex.htm LatexRender
 *
 */

class LatexHelper extends Helper
{
    var $_instance=null;
    
    var $images_url=’img/latex’; // under webroot folder
    
    var $tmp_dir=’/tmp’;
    
    /**
     * Renders the formula and returns the <IMG> tag
     *
     * @param string $formula LaTeX Formula
     * @return string <IMG> tag to the rendered image
     */    
    function render($formula)
    {
        $this->_init();
        
        $url = $this->_instance->getFormulaURL($formula);
        $alt = htmlentities($formula, ENT_QUOTES);
        $alt= str_replace(“\r”,” ”,$alt);
        $alt= str_replace(“\n”,” ”,$alt);
        
        if ($url != false)
        {
            return “<img src='”.$url.”‘ title='”.$alt.”‘ alt='”.$alt.”‘ align=’middle’ />”;
        }
        else
        {
            return ‘[unparseable or potentially dangerous latex formula]’;
        }            
    }

    function _init()
    {        
        if (! $this->_instance)
        {
            $dest_path=WWW_ROOT. $this->images_url;
            $dest_url=$this->webroot . $this->images_url;
            
            //edit class.latexrender and change the paths to your apps
            vendor(‘latexrender/class.latexrender’);
            $this->_instance= new LatexRender($dest_path,$dest_url,$this->tmp_dir);                    
        }
    }
}    
?>

Logging with source object name and function (updated) June 14, 2006

Posted by rossoft in CakePHP.
13 comments

When you log some info, get the caller filename:filenumber object_name->function_name like:

class TestsController{
function myaction()
{
$this->log('Some info here')
}
}

at error.log file :
06-06-07 10:12:09 [ /app/controllers/tests_controller.php:4 TestsController::myaction()] Error: Some info here

The code is from OT (MIT license)

You need to load this class before the original cake class. Copy to vendors/logs/logs.php. Then put in your bootstrap.php file: require_once ROOT. DS .’vendors’ . DS . ‘logs’ . DS . ‘logs.php’;

Now everywhere you can use some other functions like caller_path that returns the complete stack trace or $this->log('xxxx') that will show the caller filename:linenumber object_name->function_name()
<?php
 /**  * Extends log class. The log now shows the caller [Filename:linenumber ClassName->function()]
 *
 * @author RosSoft
 * @version 0.3
 * @license MIT 
 *
 * @link http://sputnik.pl/dev/labs/fase/ Fase Framework
 */
 
/**
 * Set to true if you want filename::linenumber at logs
 */
define('LOG_FILENAME',true);
 
/**
 * Set to true if you want objectname->function() at logs
 */
define('LOG_OBJECTNAME',true);
 
/**
 * Date format of the logs
 * 
 * Spain Locale:   'd-m-y H:i:s'
 * US Locale:      'y-m-y H:i:s'
 */
define('LOG_DATEFORMAT','d-m-y H:i:s');
 
/**
 * Logs messages to text files.
 *
 * @package        cake
 * @subpackage    cake.cake.libs
 */ 
class CakeLog
{
    /**
     * Writes given message to a log file in the logs directory.
     * Puts the calling function name
     *
     * @param string $type Type of log, becomes part of the log's filename
     * @param string $msg  Message to log
     * @param integer $skip Number of calls to skip for reaching the calling function. The default skips to the caller of Object->log
     * @return boolean Success
     */
    function write($type, $msg,$skip=3)
    {
        if (!class_exists('File'))
        {   
             uses('file');
        }
        $filename=LOGS . $type . '.log';
        $output=date(LOG_DATEFORMAT);
        if (LOG_FILENAME || LOG_OBJECTNAME)
        {
            $output.=' [  ';
            if (LOG_FILENAME)
            {
                $output.=caller_place($skip) . '  ';
            }
            if (LOG_OBJECTNAME)
            {
                $output.=caller_name($skip) . '  ';
            }
            $output.=']';             
        }
        $output.=' ' . ucfirst($type) . ': ' . $msg . "\n\n";
        $log=new File($filename);
        return $log->append($output);
    }
}
 
/**
 * Debug calling file path
 * @author Michal Tatarynowicz (Fase Framework)
 * @license http://www.opensource.org/licenses/mit-license.php The MIT License
 */
function caller_file ($skip=1)
{
    $trace = caller_backtrace($skip);
    foreach ($trace as $step)
    {
        if (@$step['file']) return $step['file'];
    }
    return false;
}
 
/**
 * Returns nicely formatted call stack
 * @author Michal Tatarynowicz (Fase Framework)
 * @license http://www.opensource.org/licenses/mit-license.php The MIT License
 */       
 
function caller_backtrace ($skip=1) {
    $trace = debug_backtrace();
    $output = array();
 
    for ($i=$skip;$i<count($trace);$i++)
    {
        $step=$trace[$i];
        if (! empty($step['file']))
        {
            if (isset($step['object']) && is_object($step['object']))
            {     
                $class = get_class($step['object']);
            }
            else
            {
                $class = @$step['class'];             
            }
            $type  = @$step['type'];
            $funct = @$step['function'];
     
            $output[] = array
            (
                'name'  => $class? $class.$type.$funct.'()': $funct.'()',
                'place' => str_replace(ROOT, '', $step['file']).':'.$step['line'],
                'file'  => $step['file'],
                'class' => $class,
                'type'  => $type,
                'funct' => $funct
            );
        }
        else if (! empty ($step['class']))
        {
            $class = str_replace(' ', '_', ucwords(str_replace('_', ' ', @$step['class'])));
            $funct = @$step['function'];             
            $output[] = array
            (
                'name'=> $class . '->' . $funct . '()',
                'place'=>'',
                'file'=>'',
                'class'=>$class,
                'type'=>'',
                'funct'=>$funct,                 
            );
        }
    }
    return $output;
}
 
/**
 * Returns caller function's full stack trace
 * @author Michal Tatarynowicz (Fase Framework)
 * @license http://www.opensource.org/licenses/mit-license.php The MIT License
 */
function caller_path ($skip=1)
{
    $parsed = caller_backtrace($skip);   
 
    $output = array();
    foreach ($parsed as $step)
    {
        list($name, $place) = array_values($step);
        $output[] = $name . '::' . $place;
    }
 
    return join( ' > ', $output );
}
 
/**
 * Returns calling method or function name
 * @author Michal Tatarynowicz (Fase Framework)
 * @license http://www.opensource.org/licenses/mit-license.php The MIT License
 */
function caller_name ($skip=1)
{
    $step = caller_backtrace($skip);
    return $step[0]['class'] . '->' . $step[1]['funct'] . '()';
}
 
/**
 * Retuns caller function's filename and line number
 * @author Michal Tatarynowicz (Fase Framework)
 * @license http://www.opensource.org/licenses/mit-license.php The MIT License
 */
function caller_place ($skip=1)
{
    $step = caller_backtrace($skip);
    if ($step[0]['place'])
    {
        return $step[0]['place'];
    }
    else
    {
        return $step[0]['name'];
    }
       
}
?>

Http Client Class June 9, 2006

Posted by rossoft in CakePHP.
5 comments

Uses Pear or Curl for a HTTP/HTTPS GET/POST request. Remembers
cookies and can do HTTP basic auth.

Pear requires the libs in vendors/pear/ . Download the libs here

Example1:
vendor(‘http_client/http_client’);
$content=http_client_get(‘http://www.google.es/search&#8217;,array(‘hl’=>’en’ , ‘q’ => ‘cakephp’ ));

Example2: (equivalent)
vendor(‘http_client/http_client’);
$client=HttpClient();
$document=$client->get(‘http://www.google.es/search?hl=en&q=cakephp&#8217;));

Use the class if you need http auth or cookies between requests.

Put this in vendors/http_client/http_client.php
<?php
/**
 * Http Client Class
 *
 * Uses Pear or Curl for a HTTP/HTTPS GET/POST request
 * (preferred engine: Curl)
 *
 * Handles Cookies & HTTP Auth
 *
 * @author RosSoft
 * @version 0.1
 * @license MIT
 *
 * If you need cookies or HTTP Auth, don’t use the
 * function wrappers http_client_get() or http_client_post()
 * Use the class instead. Example:
 * $client=new HttpClient();
 * $client->user=’username’; //the request requires basic http auth
 * $client->password=’xxxx’;
 * $client->post(‘http://example.com/login&#8217;,array(‘admin’=>’1’)); //this will remember the cookie set by that request
 * $client->get(‘http://example.com/admin/index&#8217;); //the cookie is used
 * 
 */

/**
 * Gets the content of an HTTP/HTTPS GET request
 * @param string $url Destination URL
 * @param array $params Associative array of params (don’t need to urlencode them)
 * @param string $engine Can be ‘Pear’ or ‘Curl’
 * @return string The content
 *
 */
function http_client_get($url,$params=array(),$engine=’Curl’)
{
    $client=new HttpClient($engine);
    return $client->get($url,$params);
}

/**
 * Gets the content of an HTTP/HTTPS POST request
 * @param string $url Destination URL
 * @param array $params Associative array of params (don’t need to urlencode them)
 * @param string $engine Can be ‘Pear’ or ‘Curl’
 * @return string The content
 */
function http_client_post($url,$params=array(),$engine=’Curl’)
{
    $client=new HttpClient($engine);
    return $client->post($url,$params);   
}

/**
 * Main class
 * You can use indirectly through http_client_get
 * or http_client_post wrappers or directly by:
 *
 * $client=HttpClient();
 * $document=$client->get(‘http://www.google.es/search&#8217;,array(‘hl’=>’en’, ‘q’=>’cakephp’));
 *
 * Is exactly the same as:
 * $client=HttpClient();
 * $document=$client->get(‘http://www.google.es/search?hl=en&q=cakephp&#8217;));
 *
 */

class HttpClient extends Object
{
    /**
     * Engines: ‘Pear’,’Curl’
     */
    var $_engine;

    /**
     * Http Connection timeout in seconds
     */   
    var $timeout=30;

    /**
     * Http Basic Auth
     * @var $user
     */   
    var $user=null;
   
    /**
     * Http Basic Auth
     * @var $password;
     */
   
    var $password=null;
   
    function HttpClient($engine=’Pear’)
    {
        if (!in_array($engine,array(‘Pear’,’Curl’)))
        {
            $message=”HttpClient: unknown engine $engine”;
            $this->log($message,LOG_ERROR);
            die($message);           
        }
        $engine=’HttpClient’ . $engine;
        $this->_engine=new $engine($this);
    }
   
    /**
     * Gets the content of an HTTP/HTTPS GET request
     * @param string $url Destination URL
     * @param array $params Associative array of params (don’t need to urlencode them)
     * @return string The content
     */
   
    function get($url,$params=array())
    {
        return $this->_engine->get($url,$params);
    }

    /**
     * Gets the content of an HTTP/HTTPS POST request
     * @param string $url Destination URL
     * @param array $params Associative array of params (don’t need to urlencode them)
     * @param string $engine Can be ‘Pear’ or ‘Curl’
     * @return string The content
     */   
    function post($url,$params=array())
    {
        return $this->_engine->post($url,$params);
    }

    /**
     * Returns information of last response
     * (the content of the array is engine dependant)
     * @return array
     */   
    function response()
    {
        return $this->_engine->response();
    }
       
   
}

/**
 * Engine Client: Pear
 */
class HttpClientPear extends Object
{
    var $_client;
    var $_response;
   
    function __construct($parent)
    {
        $this->_parent=$parent;
        vendor(‘pear/init’); //does an ini_set for include_path
        vendor(‘pear/HTTP/Client’);
        $params=array(‘timeout’=>$this->_parent->timeout);
       
        if ($this->_parent->user!==null)
        {
            $params[‘user’]=$this->_parent->user;
            $params[‘password’]=$this->_parent->password;
        }
        $this->_client = new HTTP_Client($params);
    }
   
    function get($url,$params=array())
    {       
        $this->_client->get($url, $params);
        return $this->_execute();
    }
   
    function _execute()
    {
        $this->_response=$this->_client->currentResponse();
        return $this->_response[‘body’];
    }
   
    function post($url,$params)
    {
        $this->_client->post($url, $params);
        return $this->_execute();
    }
   
    function response()
    {
        return $this->_response;
    }       
}

/**
 * Engine Client: Curl
 */
define(‘HTTP_CLIENT_CURL_COOKIES’,CACHE . ‘http_curl_cookies.txt’);
class HttpClientCurl extends Object
{
    var $_client;
    var $_response;
   
    function __construct($parent)
    {
        $this->_parent=$parent;
        file_put_contents(HTTP_CLIENT_CURL_COOKIES,”);
               
    }

    function get($url,$params)
    {
        $this->_init();
        if (count($params))
        {
            $url=$url . ‘?’ . $this->_convert_params($params);
        }
        curl_setopt($this->_client, CURLOPT_POST, 0);
        curl_setopt($this->_client, CURLOPT_URL, $url);
        return $this->_execute();           
    }
   
    function post($url,$params)
    {
        $this->_init();
        curl_setopt($this->_client, CURLOPT_POST, 1);
        curl_setopt($this->_client, CURLOPT_POSTFIELDS, $this->_convert_params($params));
        curl_setopt($this->_client, CURLOPT_URL, $url);
        curl_setopt($this->_client, CURLOPT_FOLLOWLOCATION, 1);                   
        $result=$this->_execute();
        return $result;
    }
   
    function response()
    {
        return $this->_response;
    }
   
    function _execute()
    {       
        $body= curl_exec($this->_client);               
        $this->_response=curl_getinfo($this->_client);
        curl_close($this->_client);
        return $body;
    }
    function _convert_params($params)
    {
          $array= array();
        foreach ($params as $name=>$value)
        {
              $array[] = “$name=”.urlencode($value);     
        }
        return implode(“&”, $array);
    }
   
    function _init()
    {
        $this->_client= curl_init();
        if (! $this->_client)
        {
            die(‘HttpClientCurl: curl_init() fails’);
        }
        curl_setopt($this->_client, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($this->_client, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($this->_client, CURLOPT_TIMEOUT,$this->_parent->timeout);          
           curl_setopt($this->_client, CURLOPT_COOKIEFILE,HTTP_CLIENT_CURL_COOKIES);
           curl_setopt($this->_client, CURLOPT_COOKIEJAR,HTTP_CLIENT_CURL_COOKIES);
          
           if ($this->_parent->user !== null)
           {
               curl_setopt($this->_client, CURLOPT_USERPWD, “{$this->_parent->user}:{$this->_parent->password}”);
           }
    }
           
}
?>

Rounded Corners Helper June 4, 2006

Posted by rossoft in CakePHP.
16 comments

A helper for making rounded corners in DIV elements

Uses the javascript library CurvyCorners

Needs the HeadHelper

1. Copy curvycorners files to app/webroot/js/corners/ (the file needed is app/webroot/js/corners/rounded_corners_lite.inc.js )

2. Copy this to app/views/helpers/corners.php


<?php
/**
 * Corners Helper
 *
 * Rounded corners with Javascript
 * 
 * @author RosSoft
 * @version 0.1
 *
 * Requires CurcyCorners
 * @link http://www.curvycorners.net
 *
 * Requires Prototype
 */

class CornersHelper extends Helper
{
    var $_library; //static array of items to be rounded
    var $helpers=array('Javascript','Head');            

    function __construct()
    {
           static $library=array();  //for php4 compat
           $this->_library=& $library;
    }
   
    /**
     * Create the rounded corners for one element
     * 
     * @param string $id Dom ID of the element to be rounded
     * @param mixed $radius. Can be a global radius or an hash array
     * of (tl=>top_left, tr=>top_right, bl=>bottom_left, br=>bottom_right) radius values
     *     example: $radius=array('tr'=>25,'bl'=>20)
     * @param boolean $anti_alias Changing the value of antiAlias from true to false will toggle the anti-aliasing. Turning off anti-aliasing will reduce the smoothness of the corners but will greatly increase the rendering time required to draw the corners.
     * @param boolean $auto_padding If you set autoPad to true, the curvyCorners script will automatically create the inner DIV for you and move all your content from the main DIV into the inner DIV and apply any padding you have specified in your stylesheet for the outer DIV.         
     */
    function corners($id,$radius=20, $anti_alias=true, $auto_pad=true)
    {
        if (is_array($radius))
        {
            array_merge(array(    'tl'=>null,
                                'tr'=>null,
                                'bl'=>null,
                                'br'=>null), $radius);                                       
        }
        else
        {           
            $radius_value=$radius;       
            $radius=array(    'tl'=>$radius_value,
                            'tr'=>$radius_value,
                            'bl'=>$radius_value,
                            'br'=>$radius_value);
        }
        $options=$this->_make_options($radius,$anti_alias, $auto_pad);
        $this->_library[]=array('id'=>$id , 'options'=>$options);       
    }
   
   
    /**
     * Makes an array of options compatible with js lib (needs JSON conversion)
     *
     * @param array $radius. Hash array 'tl','tr','bl','br' keys with radius value
     * @param boolean $anti_alias
     * @param boolean $auto_pad
     */
    function _make_options($radius,$anti_alias,$auto_pad)
    {
        $options=array(    'antiAlias'=>$anti_alias,
                        'autoPad'=>$auto_pad);
        foreach (array('tl','tr','bl','br') as $key)
        {
            $options[$key]['radius']=$radius[$key];
        }
        return $options;           
    }
   
    function afterRender()
    {
        if (!empty($this->_library))
        {
            $js_init='';           
            foreach ($this->_library as $elem)
            {
                $js_init.="var curvyobj=new curvyCorners(" . $this->Javascript->object($elem['options']) . ",$('{$elem['id']}'));";
                $js_init.="curvyobj.applyCornersToAll();";
            }
           
            $this->Head->register_js('corners/rounded_corners_lite.inc.js');   
            $this->Head->register_onload($js_init);
        }           
    }
}
?>