Environment bootstrapping with Titon\Environment

Handling multiple environments in an application can be tedious as each environment supplies its own set of configuration and unique bootstrapping. The amount of environments can grow to unmanageable numbers when dealing with dev environments, for example, different configuration for each developer. With no external dependencies, and a basic requirement of PHP 5.3, the Titon\Environment package hopes to solve this problem.

Defining Environments

An environment is mapped using an HTTP host or an IP address. During an HTTP request, an environment will be detected that matches the current host/IP, which is then loaded and bootstrapped. But first the environments need to be added through addHost(), which accepts a unique key, an array of hosts and IPs to match against, and finally the type. Out of the box, the package supports 3 types of environments — dev, staging and prod.

use Titon\Environment\Environment;
use Titon\Environment\Host;
// Create an environment instance
$env = new Environment();
// Register a dev env
$env->addHost(new Host('dev', array('127.0.0.1', '::1', 'localhost'), Environment::DEVELOPMENT));

What this did is create a development host environment for the localhost host, the 127.0.0.1 v4 IP, and the ::1 v6 IP. When either of those values are matched against the current request, the environment will be bootstrapped. But where does the bootstrapping take place? We must define it first! This can be accomplished through the setBootstrap() method on the Titon\Environment\Host object. Using the same example above, an absolute path to a bootstrap file can be defined.

$env->addHost(new Host('dev', array('127.0.0.1', '::1', 'localhost'), Environment::DEVELOPMENT))
	->setBootstrap('/absolute/path/to/bootstrap.php');

Now when an environment is detected a bootstrap will occur. This bootstrap file should contain configuration and logic specific to each environment.

Multiple Environments

In the previous example only one environment was added, dev. In standard applications, at minimum 2 environments will exist, dev and prod. Let's add the prod environment through the production domain and include a fallback. A fallback is used when an environment cannot be matched — this usually will fallback to prod as there is no risk of dev code making it into production, or at minimum a dev environment with error reporting turned off.

$env->addHost(new Host('prod', 'website.com', Environment::PRODUCTION))->setBootstrap('/envs/prod.php');
// Set a fallback using the host key
$env->setFallback('prod');

Let's now solve the multiple developer issue mentioned in the opening paragraph.

// Share the default boostrap
$env->addHost(new Host('john', 'john.dev.website.com', Environment::DEVELOPMENT))->setBootstrap('/envs/dev.php');
$env->addHost(new Host('mike', 'mike.dev.website.com', Environment::DEVELOPMENT))->setBootstrap('/envs/dev.php');
// Custom boostrap
$env->addHost(new Host('chris', 'chris.dev.website.com', Environment::DEVELOPMENT))->setBootstrap('/envs/dev-chris.php');

Or perhaps multiple QA staging environments for different regions?

foreach (array('us', 'eu', 'cn', 'au') as $region) {
	$env->addHost(new Host('qa-' . $region, $region. '.qa.website.com', Environment::STAGING))->setBootstrap('/envs/qa-' . $region. '.php');
}
Initializing An Environment

Once all the environment hosts have been defined, the package must be initialized. This can be done through the initialize() method, which will kick-start the matching and bootstrapping process.

$env->initialize();

Once an environment has been chosen, you can access it at anytime.

// Return the current host
$env->current();
// Or values from the host
$env->current()->getKey();
$env->current()->isStaging();
// Or use the convenience methods
$env->isDevelopment();
In Closing

For being such a lightweight class, the Titon\Environment package provides an extremely helpful application pattern. Be sure to give it a try in any MVC framework!

Event handling with Titon\Event

Event-driven programming is a popular paradigm that is used in nearly every application. It allows for the manipulation or alteration of an applications process flow. Titon provides a very light-weight and robust event system through the aptly named Event package, which is based off of the observer pattern and requires PHP 5.3. Using the package however, is rather straight forward.

Registering observers

Before an event can be emitted (or dispatched depending on your background), an observer must be registered. In Titon, there are 2 kinds of observers: callbacks which utilize closures or callable references, or listeners which are instances of a class object. Two different approaches that generate the same result in the end. The register() and registerListener() methods can be used to register observers.

The register() method accepts 3 arguments: $event, $callback, and an array of $options (optional). The name of the event is usually a unique dot notated string that represents its purpose. The callback can either by an instance of a closure, an array that contains an object instance and a method name, or the name of a global function. Read up on the callable type for more examples.

use Titon\Event\Event;
use Titon\Event\Emitter;
// Create an emitter instance
$event = new Emitter();
// Register a closure
$event->register('app.beforeDispatch', function(Event $event) {
	// Execute some logic
});
// Or register a callback
$event->register('app.afterDispatch', [$object, 'methodName']);

The third argument accepts an array of options or an integer. Passing an integer is a shortcut for setting the event priority level. The following options are available.

  • priority (int:100) - The order in which observers will be emitted in ascending order
  • overwrite (bool:false) - Overwrite an observer with the same priority level, else shift down
  • once (bool:false) - Emit the observer once and then remove itself from the list
  • exit (bool:false) - Automatically stop the emit process if an observer returns a falsey value
$event->register('app.run', [$foo, 'methodName'], 50); // Register with a priority of 50
$event->register('app.run', [$bar, 'methodName'], 50); // Will shift to 51 since overwrite is false
// Supply multiple options
$event->register('app.run', [$baz, 'methodName'], [
	'priority' => 50,
	'overwrite' => true,
	'once' => true,
	'exit' => true
]);

Registering listeners is slightly different...

Handling listeners

Listeners are class instances that implement the Titon\Event\Listener interface, which in turn registers multiple observers for events when the class instance itself is registered. The interface provides a single method, registerEvents(), which must return a mapping of event to callbacks.

When returning an array, the array key must be the name of an event, and the value should be an array of options or a method name. The available options can be found above for register(). Each event can support a single callback, or multiple callbacks. The following example demonstrates all the possible combinations.

use Titon\Event\Event;
use Titon\Event\Listener;
class Foo implements Listener {
	public function registerEvents() {
		return [
			// Register a single method with no options
			'app.beforeDispatch' => 'beforeDispatch',
			// Register a single method with options
			'app.afterDispatch' => ['method' => 'afterDispatch', 'priority' => 30],
			// Register multiple methods with and without options
			'app.run' => [
				'run',
				['method' => 'runOnce', 'once' => true, 'exit' => true]
			]
		];
	}
	// Define the methods being registered.
	public function beforeDispatch(Event $event) {}
	public function afterDispatch(Event $event) {}
	public function run(Event $event) {}
	public function runOnce(Event $event) {}
}

Once the interface has been implemented, the class object can be registered in the Emitter.

$event->registerListener($listener);
Removing observers

Removing a registered observer can be quite tricky, as the original callback must also be used to remove it. When removing a closure, the same instance closure must be used. When removing a callable, the same reference must be used. When removing a listener, the same class instance must be used. The following example demonstrates this.

$closure = function(Event $e) { };
$event->register('event.foo', $closure);
$event->remove('event.foo', $closure); // Same reference
$event->register('event.foo', [$object, 'method']);
$event->remove('event.foo', [$object, 'method']);
$listener = new Foo();
$event->registerListener($listener);
$event->removeListener($listener); // Same reference
Convenience methods

Similar to popular JavaScript frameworks, a few convenience methods exist for observer registering and removing, they are on(), off(), and once(). The on and off methods accept either a callable or a listener, allowing for easier integration. The argument list for these methods is exactly the same as their counterparts.

// Register
$event->on('event.foo', [$object, 'method']);
$event->on('event.foo', $listener);
// Register once
$event->once('event.foo', function(Event $e) {});
// Remove
$event->off('event.foo', [$object, 'method']);
$event->off('event.foo', $listener);
Emitting events

Once observers have been registered, an event can be emitted to loop through and execute its observers. This can be done through the emit() method. The response when emitting will either be a Titon\Event\Event object, or an array of objects, depending on the event scope.

$response = $event->emit('event.foo');

An array of parameters can be supplied as a second argument to emit(), which will be passed on as arguments to the observers. Parameters can also be passed by reference allowing observers to modify data.

$response = $event->emit('event.foo', [&$data, $flag]);
// Available as arguments in the observer callback
function(Event $event, array &$data, $flag) {
	$data['foo'] = 'bar';
}

Multiple events can also be emitted when separating names with a space.

$responses = $event->emit('event.foo event.bar');

Or multiple events can be emitted by using a wildcard. This will resolve all event names that fall under that path.

$responses = $event->emit('event.*');
The Event

When an event is emitted, a Titon\Event\Event object is created, passed through all the observers, and finally returned as a response. The object represents the current state of the event and provides functionality for managing that state — the most popular being the stopping of propagation.

When the propagation is stopped, the event will exit out of the observer loop and cease processing. This can be done by calling stop() on the event object within an observer.

function (Event $event) {
	$event->stop();
}

Communication between observers can also be quite complicated (or never implemented). Titon provides a way of persisting data through the event object using setData() and getData(). This allows for an observer to set data at the beginning of the loop, which in turn can be used by another observer at the end of a loop.

function (Event $event) {
	$event->setData(['foo' => 'bar']);
}
function (Event $event) {
	$data = $event->getData();
}

Data can also be set by returning a value from the observer.

function (Event $event) {
	return ['foo' => 'bar'];
}

There are also times when the priority order of the loop is uncertain. The getCallstack() method can be called to return an array of all observers in order of priority. The getIndex() method returns that position in the loop (not the priority level), and the getTime() method returns the timestamp of when the event started.

Emittable trait

As mentioned above, the package itself requires PHP 5.3. However, there is a PHP 5.4 trait available, Titon\Event\Traits\Emittable, that provides an event managed layer within a class. This allows events to be registered and emitted in context to that class; very useful for classes that require callback hooks. The trait provides the following methods: getEmitter(), setEmitter(), on(), off(), and emit().

use Titon\Event\Traits\Emittable;
class Foo {
	use Emittable;
	public function doSomething() {
		$this->emit('foo.do');
	}
}
$foo = new Foo();
$foo->on('foo.do', [$object, 'method']);
$foo->doSomething();
In closing

The Titon\Event package is very powerful and provides additional functionality that other event dispatching libraries do not provide. Some of these features include:

  • Multiple event resolving
  • Wildcard event resolving
  • Event data persistence through the object or observer return
  • Automatic propagation stopping through the exit option
  • Observer once execution through the once option
  • Observer call stack generation
  • Observer priority overwriting
  • Class layer event management through Emittable trait
  • And many more awesome features

So give it a whirl and let me know what you think!

Changelogs: Decoda v6.2.0

A new version of Decoda has been released, version 6.2.0. Please download the new tag or view the documentation. If you have any questions, be sure to send me an email or comment on this post. If you run into any problems, report an issue on the GitHub repository.

Version: 6.2.0
Tested On: PHP 5.4.3, Composer
Requires: PHP 5.3, Composer
Commit Hash: e90e79fddb88aff3928eb74018594b5717080e1b
Changes:

  • Added a newline to line break conversion setting lineBreaks [#48]
  • Added an onlyTags setting to filters that only allow tags and no text nodes as direct descendants
  • Added [*] list item tag to ListFilter (does not support nested lists)
  • Changed utility methods to public from protected
  • Improved newline normalization

Prism PHP Support

If you follow me on GitHub you may have noticed that I forked and committed quite a bit to the wonderful Prism JS project by Lea Verou. If you don't know what Prism is, it's a lightweight syntax highlighter written in JavaScript. It's what powers the syntax highlighting for my code blocks.

My major contribution to the project was full PHP highlighting support (including 5.3 and 5.4), as well as some improvements to the C-Like language and the inclusion of the CSS extras language. Take a look at the PHP snippet below which is taken from my Titon\Model project.

/**
 * @copyright	Copyright 2010-2013, The Titon Project
 * @license		http://opensource.org/licenses/bsd-license.php
 * @link		http://titon.io
 */
namespace Titon\Model;
use Titon\Common\Base;
use Titon\Common\Traits\Attachable;
use Titon\Model\Relation;
class Model extends Base {
	use Attachable;
	protected $_config = [
		'connection' => 'default',
		'table' => '',
		'prefix' => '',
		'primaryKey' => 'id',
		'displayField' => ['title', 'name', 'id'],
		'entity' => 'Titon\Model\Entity'
	];
	public function addRelation(Relation $relation) {
		$this->_relations[$relation->getAlias()] = $relation;
		$this->attachObject([
			'alias' => $relation->getAlias(),
			'class' => $relation->getModel(),
			'interface' => 'Titon\Model\Model'
		]);
		return $relation;
	}
	public function createTable(array $options = [], $temporary = false) {
		$schema = $this->getSchema();
		$schema->addOptions($options + [
			Dialect::ENGINE => 'InnoDB',
			Dialect::CHARACTER_SET => $this->getDriver()->getEncoding()
		]);
		return (bool) $this->query(Query::CREATE_TABLE)
			->attribute('temporary', $temporary)
			->schema($schema)
			->save();
	}
	public function getPrimaryKey() {
		return $this->cache(__METHOD__, function() {
			$pk = $this->config->primaryKey;
			$schema = $this->getSchema();
			if ($schema->hasColumn($pk)) {
				return $pk;
			}
			if ($pk = $schema->getPrimaryKey()) {
				return $pk['columns'][0];
			}
			return 'id';
		});
	}
}

The language also supports seamless integration with HTML markup. The snippet below is taken from my Titon\Debug project.

<div class="titon-debug">
	<div class="debug-head">
		<abbr title="<?php echo $file; ?>" class="debug-file">
			<?php echo self::parseFile($file) . ':'; ?><!--
			--><span class="debug-line"><?php echo $line; ?></span>
		</abbr>
	</div>
	<?php foreach ($vars as $var) { ?>
		<div class="debug-output">
			<?php if (isset($dump)) {
				echo self::_renderTemplate('table', array('value' => $var));
			} else { ?>
				<pre><code><?php echo \Titon\esc(print_r($var, true)); ?></code></pre>
			<?php } ?>
		</div>
	<?php } ?>
</div>

I hope you all find use in this contribution! Feel free to report any issues or suggestions to me.

Wordpress: a fractal of bad design

Everyone knows about WordPress, and everyone knows WordPress is just awful. If you happen to think WordPress is awesome, hopefully your mindset will change by the end of this post. I'll just state this now — I hate WordPress. I despise its popularity but I understand why it is so, I want to shoot myself when I look at the source code, and I try my hardest to avoid any WordPress related projects. But the worst problem of all is it breeds ignorance (e.g., "WordPress developers"), promotes backwards standards and implements inefficient design patterns.

Inspired by eevee's post on PHP: a fractal of bad design, this post will attempt to do the same about WordPress. Any point eevee made about PHP, double that for WordPress. I'll try my best not to bash everything, but it's really hard not to. I've literally revised this post 4 times to tone it down.

I've worked with WordPress through the years, even way back in 1.x days. I recently had to maintain a few 3.5 and 3.6 installs for my previous employer, as well as interact with their VIP service (but that's a whole topic in itself). I've written plugins, widgets, modified the admin, yadda yadda. I'd say I have a good grasp on WordPress as a whole, and that's why I'm writing this post. I always get asked by other developers why I hate WordPress, so here's a definite list of the largest problems with the WordPress codebase, but I'm also not the only one who thinks so.

Before I begin, I would like to state that popularity does not equate to quality. Stop using that as an excuse to use WordPress.

Please read the post before bashing me or trying to defend. If I can tell your comment was posted before reading it, I will delete it, else have at it!
Globals variables are good right?

No, they are bad and should rarely be used. WordPress uses them everywhere for everything; the loop for example. The problem with globals is that they are mutable, which permits them to be modified and overwritten, which in turn will cause problems in other unrelated areas of the application. Another issue is determining the original source location, as it's near impossible to deduce which part of the application created the variable nor what overwrote it during the interpretation cycle. I could easily disrupt the loop by writing the following code:

global $post;
$post = null;

And voilĂ , the whole application is useless. Honestly, I cannot wrap my head around the thought process on thinking this was a good idea. Globals (whether variable or state related) have a use case, WordPress obviously doesn't know which case that is.

Is database abstraction really necessary?

Yes, it definitely is. WordPress has no concept of models or entities (or very poor ones like WP_Post), nor does it use an ORM or ActiveRecord type of pattern. All database access is divided up into separate query objects, like WP_Query and WP_User_Query, all of which have insane amounts of inefficient logic to handle paging, filtering, sanitizing, relations, etc. To top it all off, each time a query is made it modifies a global object (refer to the point above), wtf? The codebase provides functions like query_posts() and get_posts(), all of which call WP_Query internally. Like honestly, why would a query need to be global?

function query_posts($query) {
	$GLOBALS['wp_query'] = new WP_Query();
	return $GLOBALS['wp_query']->query($query);
}

Database queries are used on a case by case basis, they shouldn't persist. Each query class and function may do something slightly different, but redundancy much? This problem would not exist if a proper DBAL was in place. WordPress does have a wpdb class that tries to mimic a DBAL, but falls short.

The biggest issue (in my opinion) with their system is that it doesn't allow for custom database tables. WordPress for some reason wants to store any and all data in their tables, most of which is handled through custom post types, meta tables, or options tables. One could create custom database tables through a series of actions and callbacks, but there would be no admin interface, how stupid is that? Furthermore, there is no direct database access since WordPress doesn't support an abstraction layer (this again?), so you're stuck with literal PDO and MySQL calls.

Throwing everything in a custom post type or a taxonomy is not the solution, it is the problem.
At least routing is done with mod_rewrite!

Using mod_rewrite isn't a bad thing, but modifying mod_rewrite rules through the PHP layer is. WordPress updates an .htaccess file anytime the core system or a plugin adds or modifies rewrite rules — only after you click a generate permalinks button within the admin (a source of many debugging headaches). Routing libraries like Horde, Symfony and even my own Titon have been a concept around for a very long time, so I'm not going to really delve into them. However, there's many issues with WordPress's routing system, too granular for me to get into, but most if not all of the issues would be solved by a decent PHP layered router.

All of the "necessary" functions like is_page(), is_single(), and is_category() would be obsolete as the router would handle all of the mapping, scoping, parameters, and arguments. For an idea on how bad rewriting is, just look at the docs.

What about the file and namespacing architecture?

I'm on the edge about this issue, seeing as how the WordPress codebase is quite old. Since WordPress was released 10 years ago (yeah, sadly), the concepts of MVC and proper data modeling weren't widely known or used, so the default PHP standard of laying out files was implemented. This primarily refers to themes in that each page is represented by a corresponding PHP file — index.php, archive.php, single.php, etc — instead of routing (point above) to a class instance that represents the state and behavior of a page. So for legacy reasons, I will leave this as a non-issue, but mention it anyways.

However, the rest of the codebase is poorly architect-ed. The first problem is that most functionality is handled through global functions (which is bad, point above) and not packaged into classes. Why things should be packaged into classes or namespaces is already a pretty common standard, so I won't get into that. To continue, WordPress has been slowly adding more classes with each version, like WP_Http and WP_Object_Cache, so kudos to them, but the organization of files and classes on the file system is still rather poor.

Every class and function can be found in wp-includes, which attempts to organize them into some random file based on their behavior, functionality, and scope, so at least they tried?

Well even with a poor file system, the templating works fine

Heh, templating in WordPress? What. This was the worst part of integrating any theme, by far. All I can do is face palm at the fact that WordPress has no templating system. But PHP itself is a templating language? Well yeah, but it's not utilized in a way where it can be considered one. There is no template hierarchy, no layouts, no wrappers, no re-usable elements (also called partials), no automatic escaping, no helper libraries, no compilation or caching (albeit a proper one), no proper data model, and the list goes on and on. I've seen too many projects where I would see the same few lines of code repeated over and over 30 times... when this problem could of been solved by a single re-usable template.

WordPress has been around for 10 years. Smarty has been around for 11 years. Twig has been around for 3 years. There's not a single excuse why a third-party library, or even a custom built library isn't part of WordPress core. The very fact that templates require get_header(), get_sidebar(), and get_footer() is pathetic.

The action and filter hooks seem pretty powerful

Yes, powerfully dangerous. Let's ignore the fact that actions and filters are nearly the same exact thing, just with different verbiage.

function add_action($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
	return add_filter($tag, $function_to_add, $priority, $accepted_args);
}

Let's also ignore the fact that these are events (and should be called as such), or that the propagation cycle cannot be stopped. Even Symfony, Zend and my own Titon have a proper event handling system.

The major problem is that these hooks are used anywhere and everywhere, which allows every little part of WordPress to be altered or modified. Any part of the application, whether it be a plugin, a library, or user implemented, can set a hook that modifies data, alters logic, or causes a problem later on in the execution cycle. Another major problem is that arguments are filtered (refer to $accepted_args above) unless strictly defined otherwise. In what case would I not want all arguments? None. Both of these issues lead to a debugging nightmare.

The hook system would be used scarcely if WordPress was architect-ed correctly. Hooks shouldn't exist in this magnitude.

What about error handling?

Get this, instead of using the built-in PHP error and exception handling, WordPress wrote their own! Ridiculous right? Instead of throwing exceptions and allowing the developer to catch and handle it (like any sane application), WordPress returns (not throws) an instance of WP_Error — a class that implements messages and codes, you know, like an exception.

What's even more ridiculous is that some functions can toggle a flag that either returns the WP_Error or returns a falsey value instead (I'm looking at you wp_insert_post() and wp_update_post()). This feature is so terrible, it speaks for itself.

But plugins and themes are where the magic happens

Take everything I said above, and everything I will continue to say, and double that. That's what those are. With their awful and inconsistent file architecture, no consistency between each other, their abuse of actions and filters, their inefficient database access, their backwards standards, and overall shoddy code, plugins and themes are an even greater problem then WordPress itself. When a problem arises from either core or another plugin, they believe throwing another plugin at it will fix the issue. The fact that so many plugins exist for such little things, and how different themes are from each other, just shows how poorly written WordPress is to begin with.

Anything else?

I haven't written about everything that irks me yet, so I will summarize a few more points below. I'm positive there are many more issues, but these should suffice for now. Feel free to send me more and I will append them to this post.

  • Class Abstraction and Inheritance - Very poorly done or simply not done at all.
  • Debugging Tools - There are no WordPress specific debugging tools to solve the problems listed above. And before you say anything, the Debug Bar is not a tool; displaying environment data isn't debugging.
  • Backwards Standards - Instead of following the rest of the PHP world with PSR or PEAR standards, WordPress decided to write their own standard which is quite opposite.
  • Asset Handling - JavaScript and CSS is handled through some kind of queuing and packaging system. Yet in the end, the source is just littered with script blocks and link tags. No compression, no concatenation, no optimization.
  • Deprecated Code - WordPress triggers deprecation errors for outdated code. Either it should be exceptions or they should require developers to do a hard migration.
  • Echo vs Return - Functions should never echo code, it should always return. This isn't as bad as the WordPress 1.x days, but it's still on my list of issues.
  • Pseudo Cron Jobs - Instead of using actual cron jobs, WordPress implemented a PHP layered cron job system. Which basically saves callback references in the database and triggers them from the PHP request at certain events. Lol what?
  • Over-zealous Escaping - Let's throw escaping and filtering functions around everything!
  • Security & Uptime - My friends over at MediaTemple would disagree. Most of their support issues or server problems are WordPress related.
  • Option Autoloading - The "autoload" of wp_options for the core and all plugins is extremely inefficient and slow.
  • Data Storing - Throwing all post related data in wp_postmeta or non-post related data in wp_options. You'll eventually end up with MBs of data when in actuality it should be far less. Can become terribly slow to sort through and read from.
  • Featured Images - One can define multiple image sizes for the featured image. However, all of those sizes are generated regardless of which ones you want, with no way to choose.
  • Menus - Creating hierarchical menus work well enough. But trying to render the menus that veer from the original implementation is complicated. What is this terrible Walker class? Give me templates.
So what's the solution?

WordPress has dug themselves into a pretty big hole, one that's not possible to get out of unless they do a complete rewrite of the system. It's been 10 years, and honestly, I don't ever see that happening. They continue to shoot themselves in the foot by expanding the system outside the scope of a blog, ala trying to make it a CMS. So what are the alternatives?

Personally, I'm pretty excited for Ghost, a blogging platform built in Node.js. Besides using Node.js as the backend, they utilize Handlebars for templating, SQLite for database storing, NPM for packaging, and localization with Polyglot.js. Already 5 reasons why it would be better than WordPress.

However, here are a few systems that are currently available. Craft is a popular up and coming CMS powered by the Yii framework. Serendipity also seems to be a popular PHP powered alternative. MovableType if you don't mind using Perl instead of PHP. Or SquareSpace if you prefer using a service.

Secondly, how about learning more of the language you are developing in? Throw together a few Laravel bundles, Symfony bundles, Titon modules, or one of the many other PHP frameworks out there. Honestly, will someone do this already and wipe WordPress off the map?

Don't let WordPress breed you into an incompetent developer.

Amazon S3 testing in Travis CI

I recently started integrating Travis CI into some of my projects. Travis is a continuous integration service that will execute your test cases in multiple different environments. I've been putting off integrating Transit into Travis because of Amazon S3 testing, but I found the time to dig into it and implement it successfully.

Preparing S3

To integrate S3 into Travis, the AWS environment must be prepared. This section can get quite lengthy and complicated, but I will keep it brief and simply. The first step is to create a new bucket and apply a lifecycle rule of 1 day to the entire bucket. This rule will delete all files in the bucket every 24 hours, which in turn requires no maintenance, cleanup and low costs on our end.

The next step is to create a new IAM group and assign the "Amazon S3 Full Access" policy permission. Once created, we need to modify the policy and change the permissions. I used the following policy for testing, which allows full S3 access to the ci-testing bucket.

{
	"Statement": [
		{
			"Effect": "Allow",
			"Action": "s3:*",
			"Resource": "arn:aws:s3:::ci-testing/*"
		}
	]
}

After the group is setup, create a new user and assign it to the group. Be sure to save/store the credentials (access and secret key) of the user as we will need them later on. It's also a good idea to set/generate a password for this user.

Integrating Travis

To make use of S3 in Travis, the appropriate variables must be set in the environment. This can be accomplished using the env:global: setting in the .travis.yml file. The region and bucket can be set using plain text.

env:
  global:
    - AWS_S3_REGION="us-east-1"
    - AWS_S3_BUCKET="ci-testing"

Like any smart developer, the access and secret keys should not be set using plain text, as this would give S3 access to anyone viewing the Travis logs. However, we can still set the private keys using encryption keys. This process requires Ruby and RubyGems to be installed (either with Cygwin, Homebrew or another package manager).

Installing RubyGems on Cygwin is rather easy. Simply download the zip and extract it to the C:/cygwin/home/ directory. Once extracted, run the following command from within the extracted folder (this requires ruby to be installed via the Cygwin setup).

ruby setup.rb

Once installed, install Travis.

gem install travis

Then use the encrypt command coupled with the -r flag (repository) to generate a secure environment key. In the example, I will be using my Transit repository.

travis encrypt -r milesj/Transit AWS_S3_KEY="<access_key>"

Add the encrypted string to the .travis.yml file. Do the process again for the secret key.

travis encrypt -r milesj/Transit AWS_S3_SECRET="<secret_key>"

The file should now look like the following:

env:
  global:
    - AWS_S3_REGION="us-east-1"
    - AWS_S3_BUCKET="ci-testing"
    - secure: "<encrypted_access_key>"
    - secure: "<encrypted_secret_key>"

These environment variables can now be accessed from the $_SERVER global within the test cases. For a full implemented example, check out my Transit library.

Changelogs: Decoda v6.0.0

A new version of Decoda has been released, version 6.0.0. Please download the new tag or view the documentation. If you have any questions, be sure to send me an email or comment on this post. If you run into any problems, be sure to report an issue on the Github repository.

Version: 6.0.0
Tested On: PHP 5.3.13, Composer
Requires: PHP 5.3, Composer
Commit Hash: 70a679488d2be76c8f997f7bdcc0555871e7bfe2
Changes:

  • Added a Component class which all Filters, Hooks, Engines and Loaders extend
  • Added a Loader class to handle resource file loading for configuration and messages
  • Added Hook::startup() to initialize data before callbacks are called
  • Added Decoda::addMessages() to add messages using a Loader
  • Added Decoda::getBlacklist() and getWhitelist()
  • Added a 2nd argument $key for Decoda::addFilter() and addHook()
  • Added a default attribute to ImageFilter (img="200x200")
  • Added a default attribute to ListFilter (list="upper-roman")
  • Added a new TableFilter
  • Added custom exceptions
  • Renamed all config methods to getConfig() and setConfig()
  • Renamed Filter::tag() to getTag()
  • Renamed Filter::tags() to getTags()
  • Renamed Engine::setPath() to addPath()
  • Renamed Engine::getPath() to getPaths()
  • Updated CensorHook to support blacklisting words using a Loader
  • Updated EmoticonHook to support adding emoticons using a Loader
  • Updated Decoda::setLocale() so that it no longer throws exceptions (can now support setting the default locale)
  • Updated Engines to support multiple template lookup paths
  • Updated with Travis CI and phpunit.xml integration

Changelogs: Decoda v5.1.0

A new version of Decoda has been released, version 5.1.0. Please download the new tag or view the documentation. If you have any questions, be sure to send me an email or comment on this post. If you run into any problems, be sure to report an issue on the Github repository.

Version: 5.1.0
Tested On: PHP 5.4, Composer
Requires: PHP 5.3, Composer
Commit Hash: b554ba63750bf2c32b0e22b757fed57d5adb981f
Changes:

  • Updated to use Multibyte extensively
  • Added Decoda::hasFilter() and Decoda::hasHook()
  • Added <code> tags within <pre> for CodeFilter and proper semantics
  • Added source tag that renders <code> tag for CodeFilter
  • Refactored Decoda::_buildTag() to be more efficient
  • Fixed bugs with custom brackets in tag parsing
  • Fixed bugs with auto-linking regex
  • Fixed bug with URLs that end in trailing slash
  • Changed <code> to <var> for var tag

Changelogs: Decoda v5.0.0

A new version of Decoda has been released, version 5.0.0. Please download the new tag or view the documentation. If you have any questions, be sure to send me an email or comment on this post. If you run into any problems, be sure to report an issue on the Github repository.

Version: 5.0.0
Tested On: PHP 5.3
Requires: PHP 5.3
Commit Hash: 29dfe2d792d65c0d2b81d470213e54e75a2db722
Changes:

  • Changed folder and namespace structure to make heavier use of Composer
  • Added attribute support for self closing tags
  • Moved interfaces to source root
  • Reversed naming of abstract classes
  • Removed Decoda's autoloader in place of Composer
  • Removed DECODA constant
  • Updated to use SPL exceptions

Using Composer in CakePHP

Composer, a magnificent dependency manager. CakePHP, a brilliant MVC framework. What's stopping you from using both in your application? Nothing at all! Personally, I have been using Composer exclusively in my applications to handle all my dependencies and even my CakePHP plugins. It's very easy to do.

The first thing you need to do is create a composer.json file within your application. It's best to place the file in app/composer.json as it would share the same directory structure with Vendor. Here's a quick example:

{
	"config": {
		"vendor-dir": "Vendor"
	},
	"require": {
		"mjohnson/uploader": "4.*",
		"mjohnson/decoda": "6.*"
	}
}

Since this is an application and not a dependency, all you need to define is the "require" property. We also set the vendor folder to use CakePHP's naming convention, because why not?

To benefit from Composer's autoloader, include the autoload.php file at the very top of Config/core.php.

require_once dirname(__DIR__) . '/Vendor/autoload.php';

Brilliant! Now we have full dependency and autoloading support. Can't get easier than that.