Codebase Upgrades: Upgrading Forum v2.x to v2.2

In the newest 2.2 version of the Forum plugin, a subscription system was added. This system allows for users to subscribe to topics and forums and then receive email notifications at specific intervals. This feature required minor database changes so a new upgrade shell was added to handle this. You will only need to run the upgrade shell if you are upgrading from 2.0 or 2.1. If you are doing a fresh install, you can ignore this post and the upgrade shell.

Begin by downloading the latest 2.2 tag and uploading and overwriting the older version. Your forum will be broken until you run the upgrade shell and the following command. Once the command is executed, simply follow the on-screen instructions.

cake -app /path/to/app upgrade
Setting up the subscription notification cron

For the subscription emails to be sent out, the subscription shell will need to be triggered. The best way to accomplish this is to setup a cron job to be triggered every 24 hours (or another time of your liking). If you are unable to setup a cron job, you will need to disable the subscription system via the forum admin settings.

0 0 * * * /path/to/cake/console/cake -app /path/to/app subscription

By default the shell will find all new activity within the past 24 hours via the database timestamp (which is separate from the cron interval time). You can change this using the timeframe parameter. For example, if you want to send emails every hour instead of every 24 hours (quotes are required).

0 * * * * /path/to/cake/console/cake -app /path/to/app subscription -timeframe "1 hour" 

Hopefully this small guide helps you upgrade to 2.2, enjoy!

Codebase Upgrades: Upgrading Uploader v2.8 to v3

The Uploader plugin has upgraded major versions from 2.8 (CakePHP 1.3) to 3.0 (CakePHP 2). Since this is not a backwards compatible change, the 3.0 version can only be used in CakePHP 2.

No more UploaderComponent

During the conversion process, the primary change is that the UploaderComponent was removed and turned into a stand-alone vendor class. This was done to further encourage the use of the AttachmentBehavior. Because of this change, you will need to manually initialize the Uploader in your Controller::beforeFilter() (only if you were using the component). The property settings are now passed as an array through the constructor.

// Before
public function beforeFilter() {
	$this->Uploader->tempDir = TMP;
}
// After
public function beforeFilter() {
	$this->Uploader = new Uploader(array('tempDir' => TMP));
}

By instantiating the class to $this->Uploader, all of the old code should still work as normal. This change also allows the possibility to use the Uploader anywhere in the application.

No more S3TransferComponent

The S3TransferComponent was trying to do something it shouldn't have. The component was removed in favor of the Vendor/S3 class that provides far more functionality. The AttachmentBehavior has also been updated to use the S3 class instead of the component.

Certain methods are now static

The bytes(), addMimeType(), checkMimeType(), mimeType() and ext() methods are now static.

Those should be the only changes you would need to make to support the new version. There are quite a few other changes, so be sure to check out the changelog and to also check out the updated documentation.

Codebase Upgrades: Upgrading AutoLogin v2.2 to v3

The AutoLogin component has upgraded major versions from 2.2 (CakePHP 1.3) to 3.0 (CakePHP 2), now 3.1. Since this is not a backwards compatible change, the 2.0 version can only be used in CakePHP 2. During the conversion process, many things had to change to support the new AuthComponent.

No more automatic username/password field detection

Previously AutoLogin would detect the custom name of your username and password fields by accessing Auth::$fields. Since that property has been removed in favor of Auth::$authenticate, the AutoLogin component received a new property called $fields which mimicked what the Auth's version did. I didn't want to magically guess what the username and password is from Auth::$authenticate and be wrong, so I made it an optional property (you should only need to change it if you are using custom fields).

$this->AutoLogin->fields = array('username' => 'user', 'password' => 'pass');

Seeing as how that was the only major change that may require backwards support, the following minor changes have also occurred: CakeRequest has been implemented anywhere $params were being referenced, the user model is detected via Auth::$authenticate (all first, then Form) in place of the deprecated Auth::$userModel and cookie support has been implemented for localhost environments (may not work in all environments).

If you have any questions, or run into any problems, let me know!

Codebase Upgrades: Upgrading AjaxHandler v1.6 to v2

The AjaxHandlerComponent has upgraded major versions from 1.6 (CakePHP 1.3) to 2.0 (CakePHP 2). Since this is not a backwards compatible change, the 2.0 version can only be used in CakePHP 2. During the conversion process, many other features were refactored or removed, here is a quick round-up.

No more automatic data gathering

In the older versions, any request data was gathered and merged into the Controller::$data property within AjaxHandler::startup(). This is no longer the case as I highly suggest using the new CakeRequest class to manipulate data in your AJAX action. This class can be accessed at Controller::$request.

No more content type restrictions

Previously you could only respond() an action using JSON, HTML, XML or text. Now you can respond using any content type defined in CakeResponse. Automatic JSON/XML formatting is still enabled.

Removed the valid() method

The valid() method was a very abstract and useless method that simply checked that strings or integers were not empty. The AjaxHandler should not be dealing with any validation (low level ones even more so), so it was removed. Please integrate your own data validation.

Besides the major changes listed above, any private class properties were converted to protected visibility, to allow class extension and the RequestHandler was replaced with CakeRequest in most situations, ala is('ajax') compared to isAjax().

If you have any questions, let me know!

Docsinated

I'm quite ashamed. I work constantly on all my PHP codebases for myself and everyone that uses them... but... I rarely update my actual documentation. After multiple emails from frustrated developers letting me know my docs make no sense and are quite outdated, I went on an update spree. I've spent the last few months updating all my codebases to a final stable release so that I may cease development on all of them (I need to free up some time). During this process I closed any outstanding issues and bugs, tagged new versions and have been recently converting all my CakePHP scripts to use the latest 2.0 version. Alongside that, I took the time to update all the documentation and changelogs on this site; the docs will always reference the latest version and any old docs have been removed.

I've always had a place in my heart for my stand-alone PHP scripts as they are very simplistic implementations of what I needed back in the day. It's quite heartening to get emails from new developers letting me know these scripts have taught them. Here's the current list:

As for my CakePHP codebases, I will constantly keep those updated if any bugs come up, but will not be adding any new features for the most part. Since CakePHP 2 was released, I had to spend some time updated all my projects. I created new branches for the old 1.3 codebase and turned the master branch into the new 2.0 repository. By doing this, it allows me to keep both CakePHP versions up to date in parallel. It also allowed me to tag new major versions for all my projects. Below are my current projects (with possibly more to come):

I hope all of you using my documentation find it useful. If you ever find something incorrect or not easily explained, be sure to shoot me an email and I will tackle it as soon as possible! Thanks for using my code :]

The widely unused and powerful setAction()

It has been quite a while since I last developed in Cake, but earlier today I spent a good 5+ hours working on some new controller internals for one of my apps. While working on this app, I thought I would give the not so used method, setAction() a spin. If you are unfamiliar with setAction(), it's a method that will internally forward one action to another. This is incredibly useful as it allows you to switch actions without having to do another HTTP request, nor having to reload all the controller and related objects; could be a huge saver on overhead.

Here's a quick example of a regular action:

public function profile($id) {
	$user = $this->User->findById($id);
	if (empty($user)) {
		$this->redirect(array('action' => 'listing'));
	}
	$this->set('user', $user);
}

What the action is doing is relatively simple. First it grabs a user based on an ID, checks to see if the result is empty, if so redirects to the users listing page, else sets the data. You could either redirect the action like I have, or you can simply throw an error message in the view saying something like "No user was found with that ID". I personally prefer redirecting in most cases, but it's a matter of preference. Lets move forward and try setAction() now.

public function profile($id) {
	$user = $this->User->findById($id);
	if (empty($user)) {
		return $this->setAction('listing');
	}
	$this->set('user', $user);
}

As you can see, I simply switched the redirect() with setAction(). Now if we visit a profile with an non-existent user, the listing action will be rendered without having to do another HTTP request. Also, do notice the use of return; this is extremely important because if you do not use return, all the logic after the setAction() will be ran and processed, causing unneeded overhead and unexpected results.

One downside that I have ran into with this approach is that the URL in the address bar does not change. But 99% of the time it won't matter as the page you render in its place will contain links to the correct pages or the user won't be copy and pasting the URL anyways.

Upgrading the forum plugin to 1.8

I have been working on the new 1.8 version of the forum for almost 2 months, simply because version 1.7 was completely broken. It used PHP 5.3 constants which didn't work on 99% of the users machines. This problem has now been corrected in the new 1.8 version, but that's not all that changed. All topics, forums and forum categories now use the sluggable behavior for pretty URLs. Furthermore, the Installer has been rewritten completely for faster and easier usage. The main purpose of this post is to direct you in the 1.8 upgrade process as there are many things you need to do.

Overwriting an older version

You will need to overwrite ALL files within the plugin. Basically every view, every model, every controller, etc has been modified in some way; primarily to support the new slugs. Sorry to all those who have changed the HTML.

Patching your plugin

The new installer system creates a legacy config file to be used for future upgrades and to maintain the current installation. If you are doing a fresh install, you can skip this step. Once you have updated all the files, you will need to patch your installation to build that config file. You can do this from within the install controller.

yourdomain.com/forum/install/patch

The patch process will require 3 things: the database config you want to use, the table prefix value (if you have one) and a checkbox that determines if you used the plugins native user table or a pre-existing one.

Upgrading the database

With the new slug changes, it requires you to upgrade your database tables with the new column and all rows to be populated with their slug equivalent. This process takes 2 steps to achieve, the first having you run an SQL command from within the installer. You can access this process at the URL below.

yourdomain.com/forum/install/upgrade_1_8

The second step requires you to use the Cake console and to run the slugify shell. This shell will populate all the empty slug rows (it can be used any time, not just the upgrade process). Your shell command may look like the following.

cake -app /path/to/app slugify
Fresh install

If you are running a fresh install, the process is extremely simply. Just go to the install controller, hit the "Begin Installation" button and follow the steps. The process will check your database for conflicts, create the tables, patch the install and create the admin user. I tried to make it as simply and straight forward as possible.

See if it's working!

Whether you did a fresh install, or patched an old one, the last thing to do is actually use the forum. You should be checking to make sure all the slugs were generated, the pages work, the links link correctly, so on and so forth. If you find a bug or problem, PLEASE report it to me so I can fix it.

Enjoy! (Sorry for the long wait time)

Setting up cron jobs with Cake shells

This post will primarily be about the process I had to go through to get cron jobs and shells working on Dreamhost. The main problem I kept running into was that the shells would simply not work at all through a cron job; yet they worked when I manually did it through SSH. The weird thing was the source of the console/cake file was being printed in my logs, instead of executed. Below is a quick checklist of things to look out for:

TERM environment variable not set

This isn't really an error, but the TERM variable is used by the server environment and does not affect the shell, even if the error is thrown. Now I may not know much about server maintenance and configuration, but adding the following code to console/cake seemed to do the trick (Hats off to Matt Curry for the tip). And of course, you should change linux to whatever your server is.

TERM=linux
export TERM
Cron environment not PHP 5

This was a weird problem I ran into. It seemed the environment in which my crons ran was PHP 4, so I was receiving unexpected results and would receive this error (it mainly depends where the php folder is located and if its loaded correctly).

console/cake: line 30: exec: php: not found

To fix this, I had to rewrite the cake console file and update it with the path to the PHP folder (path should represent your servers structure). You should only need to do this if you can't upgrade PHP to 5 for some reason.

exec php -q ${LIB}cake.php -working "${APP}" "$@"
# change to
exec /usr/local/php5/bin/php -q ${LIB}cake.php -working "${APP}" "$@"
Making sure your cron has ownership

A simple problem with a simple fix. Since the user that uploaded the console files was different than the user running the cron, I would receive this error:

sh: cake/console/cake: Permission denied

All I had to do was switch the cron user to the user that owns the file, and it fixed the permissions problem.

Making sure the cake file uses unix line endings

This was another pain in the ass that took me forever to get working. My cron environment would throw this error once I fixed the things above (this applies to my Dreamhost server and may not apply to all).

sh: cake/console/cake: /bin/bash^M: bad interpreter: No such file or directory

It required me contacting my host and asking for help on this one. I tried saving all my files as unix and made sure my FTP was not converting them in any way. Still nothing. So my host told me about this SSH command called dos2unix which would convert the file to unix line endings, and wallah, magic! Everything seemed to be working now.

dos2unix cake/console/cake

These are just a few of the minor setbacks I had to deal with when setting up my cron jobs. Most of this really applies to my Dreamhost server, as my Media Temple server worked right away with no configuration. Furthermore, here are some more Dreamhost articles that I used for this problem, that might be of aid:

Editing Your Environment Profile
Make PHP5 the Default in the Shell

Pitfalls within Cake's Cache engine

For the past week I have been working on a DataSource for the WeGame API. Everything was working fine until I added some logic to cache the results. No matter what I did I received the following fatal error:

//Fatal error: Call to a member function init() on a non-object in C:\xampp\htdocs\cake_1.2.5\cake\libs\cache.php on line 195
// Line 195
$_this->_Engine[$engine]->init($settings);

The error was being thrown from within Cache::set() and it seemed to fail because the actual storage engine object was not instantiated. I spent a good couple hours trying to track down the bug, but to no avail. I tried removing Cache::set(), placing Cache::config() within config/core.php, but still nothing.

Finally I thought to myself "Let me check my Config settings", so I did. I noticed that Cache.disable was set to true, and once I set it to false the whole system worked! Bah, pretty annoying right? I sure love Cake, but its error and exception handling isn't that great. I've ran into so many of these case scenarios where a simple trigger_error() or uncaught exception would of helped (This is for you Cake team *cough*2.0*cough*).

Another thing I would like to note, is that the path option in Cache::config() does not actually create the folder, you must do that manually. Here's how I resolved that problem.

$cachePath = CACHE .'we_game'. DS;
// Create the cache dir
if (!file_exists($cachePath)) {
	$this->Folder = new Folder();
	$this->Folder->create($cachePath, 0777);
}
Cache::config('weGame', array('path' => $cachePath));

Minimalistic approach to class getters and setters

Getters and Setters are the backbone of many PHP classes (or any programming language class), as they allow you to alter and retrieve class properties during runtime. Many well thought and powerful scripts and frameworks make use of Getters and Setters, but is there a thing as too much? Or are there better and easier alternatives? (To make it easier on me to type, and you to read, I will refer to Getters and Setters as GnS from now on.)

By now everyone should know of the Zend Framework. It's a highly customizable and robust system built with multiple components. Zend follows the pure OOP paradigm in which most, if not all classes have an abstract, an interface, and tons of GnS. For a beginner, this might look like a huge cluster of code, as well as being very large in its documentation. But for an advanced user, it could be a god send. Personally, I find Zend's use of GnS to be too much, as it can easily be trimmed down and packaged accordingly.

Before we begin, I want to outline when and how GnS should be used. GnS should be used to alter protected properties only. Why protected you ask? If a property was public, then you can just alter the property manually without the need for a median method (unless of course the method does some manipulation on the argument). If a property is private, then that property should not be altered at all during runtime, as it is data specifically generated/built by the class internally. So that leaves protected properties to act as our configurable properties.

In a typical class, when dealing with the property $_name, you would have a method getName() and setName(). Now imagine you have a class with 15+ properties; you will immediately begin to realize the scale and amount of code required to do GnS. That's where our little friends __set(), __get() and __isset() come in handy. We can easily scale down the code from 30 methods (15 for getting, 15 for setting) to 3. Take the following before and after classes:

// Using individual methods
class User {
    protected $_name;
    protected $_email;
    public function getName() {
        return $this->_name;
    }
    public function setName($value) {
        $this->_name = $value;
    }
    public function getEmail() {
        return $this->_email;
    }
    public function setEmail($value) {
        $this->_email = $value;
    }
}
// Using magic methods
class User {
    protected $_name;
    protected $_email;
    public function __get($property) {
        return (isset($this->{'_'. $property}) ? $this->{'_'. $property} : null);
    }
    public function __set($property, $value) {
        if (isset($this->{'_'. $property})) {
            $this->{'_'. $property} = $value;
        }
    }
    public function __isset($property) {
        return isset($this->{'_'. $property});
    }
}

Does that not look a lot easier? Sure the code looks a bit "hacky", but its perfectly usable and valid code. Of course, there are a few downsides related to this approach. For example, you may want to format a string before assigning it within setName(). With the magic methods you can not do so. But that doesn't stop you from creating a setName(), along side using the magic methods. Furthermore, the get syntax is different; you simply call the property (assuming there is no $name that conflicts with $_name).

// Using individual methods
$name = $User->getName();
$User->setName('Miles');
// Using magic methods
$name = $User->name;
$User->name = 'Miles';
// Times when a set method is needed
public function setName($name) {
    $this->_name = ucall($name);
}

I would like to take this a step further, as I still believe that this is too "cluttered". The next approach solves the problem of conflicting property names, as well as reducing the code required. The approach is straight forward; simply create a global $_config (or $_data, what ever suits you) property that will deal with all the getting and setting of data.

class User {
    protected $_config = array(
        'name' => null,
        'email' => null
    );
    public function __get($property) {
        return (isset($this->_config[$property]) ? $this->_config[$property] : null);
    }
    public function __set($property, $value) {
        if (isset($this->_config[$property])) {
            $this->_config[$property] = $value;
        }
    }
    public function __isset($property) {
        return isset($this->_config[$property]);
    }
}

In the end, it really boils down to the architecture of your application, and your personal coding preferences. Each approach has its pro's and con's, but the best solution (in my mind) is combining them. You would begin by creating the global $_config property, and building the magic methods. If you ever need to customize a get or set, then you create a specific method for it.