Obfuscate CakePHP Helper
Sometimes you may need to obfuscate your HTML markup. I know for one of my projects, I needed to set up a PayPal redirect page and didn’t want the data to be manipulated by any users, so I opted to obfuscate the markup using JavaScript functions. Obfuscation is by no means a perfect method for encrypting HTML markup, but it certainly makes hacking your stuff a pain, and generally gets the job done.
A CakePHP Helper
To simplify obfuscation methods for Cake, I put together the Obfuscate helper, which you can see below. Using it is easy. Copy the contents of the helper into the app/views/helpers/obfuscate.php file, then include the helper in your controller, like so:
var $helpers = array('Obfuscate');
Then in the view, whatever you want to obfuscate, simply run:
$obfuscate->string('Markup to obfuscate');
The Helper’s Code
Here’s the code you’ll need to copy to app/views/helpers/obfuscate.php:
<?php
/* Based on Ian Willis' iScramble PHP script: z-host.com/php/iscramble */
class ObfuscateHelper extends Helper {
/**
* Obfuscate::_rot13()
* ++ performs ROT13 enconding on a given string
* @param string $str The string to encode
* @access private
*/
function _rot13($str) {
$from = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$to = 'nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM';
return strtr($str, $from, $to);
}
/* Perform the equivalent of the JavaScript escape function */
/**
* Obfuscate::_escape()
* ++ Equivalent of the JavaScript escape function
* @param string $plain The string to escape
* @access private
*/
function _escape($plain) {
$escaped = "";
$passChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789*@-_+./";
for ($i = 0; $i < strlen($plain); $i++) {
$char = $plain{$i};
if (strpos($passChars, $char) === false) {
$escaped .= sprintf("%%%02X", ord($char));
} else {
$escaped .= $char;
}
}
return $escaped;
}
/**
* Obfuscate::string()
* ++ Obfuscates a given string
* @param string $plain The string to obfuscate
* @param bool $longPwn Whether to use more JavaScript code for better obfuscation
* @param bool $rot13 Whether to use ROT13 encoding; takes longer to decode, not recommended for long strings
* @param string $sorry Markup to display if the user doesn't have JavaScript enabled in the browser
* @return Obfuscated JavaScript code
* @access public
*/
function string($plain, $longPwd=false, $rot13=false, $sorry="[Please Enable JavaScript]") {
$escaped = $this->_escape($plain);
if ($rot13) {
$escaped = $this->_rot13($escaped);
}
$numberOfColumns = 10;
$numberOfRows = ceil(strlen($escaped) / $numberOfColumns);
$scrambled = "";
$escaped = str_pad($escaped, $numberOfColumns * $numberOfRows);
$password = "";
srand(time());
for ($j = 0; $j < ($longPwd ? $numberOfRows : 1); $j++) {
$availChars = substr("0123456789", 0, $numberOfColumns);
for ($i = 0 ; $i < $numberOfColumns; $i++) {
$char = $availChars{ rand(0, strlen($availChars)-1) };
$password .= $char;
$availChars = str_replace($char, "", $availChars);
}
}
$scramblePassword = str_repeat($password, $longPwd ? 1 : $numberOfRows);
$scrambled = str_repeat(" ", $numberOfColumns * $numberOfRows);
$k = 0;
for ($i = 0; $i < $numberOfRows; $i++) {
for($j = 0; $j < $numberOfColumns; $j++ ) {
$scrambled{(((int)$scramblePassword{$k}) * $numberOfRows) + $i} = $escaped{$k};
$k++;
}
}
$javascript = "<SCRIPT>\n\n</SCRIPT>\n";
$javascript .= "<NOSCRIPT>\n$sorry\n</NOSCRIPT>\n";
return $javascript;
}
}
?>
Table of Contents
I’ve been asked about my upcoming CakePHP book’s table of contents. Here’s a list of the chapters with a short description about each one. As always, you can order the book which is now available online and at a bookstore near you.
Chapter 1: Introduction
Discusses many of the main advantages for using Cake and explains the direction I will go in teaching you how to use it. Briefly explains the model-view-controller (MVC) paradigm for software development as well as the concept of inversion of control for frameworks. Key benefits of using Cake over other frameworks are also discussed.
Chapter 2: Installing and Running CakePHP
This chapter explains how to install Cake and launch a bare-bones application in your web browser. This book uses CakePHP 1.2 RC1, so the installation procedures get you running in 1.2 quickly. More detailed instructions for running Cake on a localhost setup are explained in Appendix A.
Chapter 3: Creating a To-Do List Application
All about using Cake’s scaffolding feature to create a to-do list application. Explains in more detail how MVC works in Cake.
Chapter 4: Naming Files and Designing the Database
Explains the naming conventions in Cake, best practices when naming files and resources, and how to set up the database to work with Cake. Discusses database normalization, as well as Cake’s table association parameters that manage one-to-one, one-to-many, and many-to-many data relationships. How to use models is discussed, and each of Cake’s table association methods (belongsTo, hasOne, hasMany, and hasAndBelongsToMany) are explained in detail.
Chapter 5: Creating Simple Views and Baking in the Console
Explains various methods for creating views and layouts and how to use the Bake shell script to create views and functions automatically. Gives instructions on how to get Bake working in the console as well.
Chapter 6: Customizing Views
Discusses different user interaction sequences in Cake, how to handle form submissions, and how to customize views following previously baked code.
Chapter 7: Working with Controllers and Models
The main tutorial of building a more extensive blog application begins in this chapter. Explains how to customize controllers and models to handle various methods. Model functions, like find() and read(), as well as data validation methods are explained in detail. How to build controller actions is also discussed.
Chapter 8: Implementing Ajax Features
All about the Ajax helper and how to use Ajax in Cake to building a voting mechanism into the blog’s comments section. Also explains how to do file uploads using the File utility class and jQuery.
Chapter 9: Helpers
This chapter goes into detail about using helpers, how to build custom helpers, and what’s available in Cake’s built-in helpers. Each function in the HTML and Form helpers is discussed, and all other built-in helpers are outlined as well.
Chapter 10: Routes
Building routes with arguments, reverse routing, custom expressions, magic variables, and more is explained here. Also explains how to parse files with extensions other than .php and how to dynamically render an RSS feed in Cake, with a .rss extension in the URL.
Chapter 11: Components and Utilities
Explains how to use components in Cake and discusses some built-in components. The Auth, Session, Cookie, and Email components are explained in more detail, and the ACL, RequestHandler, and Security components are outlined. Cake’s utility classes, like Configure, File, Folder, HTTP Socket are explained as well as how to use I18n and L10n classes in your application.
Chapter 12: Vendors
About how to use third-party scripts in your Cake app. Also how to use Zend Framework in Cake; the Akismet and PDF Zend components are specifically mentioned and built into the blog tutorial application.
Chapter 13: Plugins
About creating Cake plugins. This chapter explains how to build a calendar plugin for use in the blog.
Chapter 14: DataSources and Behaviors
Explains how to build custom DataSources and gives a walkthrough for building an XML DataSource. Also explains building behaviors, and gives a detailed walkthrough of the Tree and Containable behaviors. Outlines other built-in behaviors and explains how to build a custom behavior.
Chapter 15: Wrapping Up the Application
Discusses the final routines when completing a Cake project, like building landing pages, using the Pages controller, generating dynamic navigation, customizing the overall design, debugging the application, and running a Cake app on a remote host.
Appendix A: Installation Issues
Explains how to set up a localhost to run Cake on a Mac and PC, and how to run MySQL.
Appendix B: How CakePHP Compares with Other Frameworks
Compares Cake with other PHP frameworks: Zend Framework, CodeIgniter, and Symfony. Discusses why Cake is better :)
Making Your Own Template Tags and Functions in Cake
To make a long story short, I’m currently working on a project where the markup in view files needs to be simplified. I thought of using Smarty or HAML, but eventually opted for building my own template engine due to the unique circumstances of this project. The syntax has to be simple and map to actions and helper functions in the Cake app. To demonstrate, say I want to insert an image in a template view file. Normally, you do this with:
<?=$html->image('filename.gif');?>
But, in the case of my project, I’m going to alter the syntax to this:
{image('filename.gif')}
If this were only one instance, I might just run a filter during the normal rendering process. But since I know that what I’m doing will eventually affect every template view, I decided to override the default View::_render() method. You may want to do something similar or employ an outside template engine to Cake. This is especially handy for content management system projects, since they often require numerous custom methods in the views themselves. If you’re crazy enough to want to do this ;-) then let me explain how I’ve been able to pull it off.
Write the custom View object
First, you need to create a new view class to extend the current one. Create the app/views/wnr.php file. (I’m calling this “WNR” because of my own custom project, but this can be whatever you want it to be.) This new view object will pull all of its view files from a folder stored as app/views/templates, so I’ll need to specify this view path in the constructor function.
<?
class WnrView extends View {
var $tmp = null;
function __construct(&$controller) {
parent::__construct($controller);
$this->ext = '.wnr';
$this->layoutPath = 'templates';
$this->viewPath = 'templates';
$this->autoRender = false;
$this->tmp = TMP.'wnr'.DS;
}
The tmp property is a placeholder for the path to the temp file that will store the view. Cake’s method for rendering views is to pull together all of the surrounding layouts, helpers, and controller data then run the include function of the view file. Since I’m overriding this method with my own rendering functions, I’ll need to include a file of some kind, but one after I’ve parsed the view file. This leaves me with making a separate file, one that is parsed like a standard Cake view file, and this is what will eventually by the tmp property.
Run the _render method
Since the WnrView class extends the View object, I can run any of View’s functions in WnrView and they will automatically override the normal view rendering processes. In the cake/libs/view/view.php file, you’ll find the _render() function. I’ve copied and pasted this function into WnrView and will change it to work with my own parsing methods.
1 function _render($___viewFn, $___dataForView, $loadHelpers = true, $cached = false) {
2 $loadedHelpers = array();
3
4 if ($this->helpers != false && $loadHelpers === true) {
5 $loadedHelpers = $this->_loadHelpers($loadedHelpers, $this->helpers);
6
7 foreach (array_keys($loadedHelpers) as $helper) {
8 $camelBackedHelper = Inflector::variable($helper);
9 ${$camelBackedHelper} =& $loadedHelpers[$helper];
10 $this->loaded[$camelBackedHelper] =& ${$camelBackedHelper};
11 }
12
13 foreach ($loadedHelpers as $helper) {
14 if (is_object($helper)) {
15 if (is_subclass_of($helper, 'Helper') || is_subclass_of($helper, 'helper')) {
16 $helper->beforeRender();
17 }
18 }
19 }
20 }
21
22 $___compiledFile = $this->_parse($___viewFn);
23 $___data = Set::merge($___dataForView,$this->viewVars);
24 extract($___data, EXTR_SKIP);
25
26 ob_start();
27
28 if (Configure::read() > 0) {
29 include ($___compiledFile);
30 } else {
31 @include ($___compiledFile);
32 }
33
34 unlink($___compiledFile); //delete tmp file
35
36 if (!empty($loadedHelpers)) {
37 foreach ($loadedHelpers as $helper) {
38 if (is_object($helper)) {
39 if (is_subclass_of($helper, 'Helper') || is_subclass_of($helper, 'helper')) {
40 $helper->afterRender();
41 }
42 }
43 }
44 }
45 $out = ob_get_clean();
46 return $out;
47 }
The key to this function is where I’ve inserted my own methods. Almost everything is the same as View::_render() except that I’ve created my own file, $___compiledFile on line 22 and on line 23, I’ve pulled the view variables into $___data. That way, when line 24 performs extract(), any new variables that my parsing engine wants to make available to the view will get pulled in.
Line 34 deletes the temp file. The only thing left is to build the _parse() function called on line 22.
Parsing the view
Now I can parse the template file with my own specs.
1 function _parse($tpl) {
2 App::import('Core','File');
3 $view =& new File($tpl); //the view file to parse
4 $data = $view->read(); //contents of the view file
5 $_seed = md5(srand(time()*rand()));
6 $_tmp =& new File($this->tmp.$_seed); //random tmp file for compiling
7
8 /** THE ACTUAL PARSER; PUT YOUR OWN REGEX RULES HERE **/
9 preg_match_all('/\{([a-zA-Z0-9]+)\((.*?)\)\}/is',$data,$matches);
10 for($i=0; $i<count($matches[0]); $i++) {
11 $tag = $matches[0][$i];
12 $str = '<?=$html->'.$matches[1][$i].'('.$matches[2][$i].');?>';
13 $data = str_replace($tag,$str,$data);
14 }
15
16 $_tmp->write($data);
17 $_tmp->close();
18 $view->close();
19 return $this->tmp.$_seed;
20 }
Notice that this function creates the tmp file, reads the template view file, then on lines 9-14, it parses content that has {function(params)} and passes it to the HTML helper. Of course, you could multiple rules where I’ve done line 9-14, and even add data as view variables with $this->set(). I’ve actually done a lot more work mapping functions to the AppController object and from there working with models and controllers, then returning data from all over the application. This allows me to invert the typical MVC process and allow the template file to tell Cake what should be rendered. But I suppose that’s for another day.
If you wanted to use an outside vendor or parsing engine, like Smarty, then you could pass the view file contents through the vendor instead of through the _parse() function, like I’ve done here.
Using the new WnrView class
Now that it’s all in place, I only have to create the app/views/templates directory and store my own template files there. Then, I can override the normal view process by calling
$this->view = 'wnr';
in the controller actions where I want to run this templating engine.

