Clean code for WordPress

download Clean code for WordPress

If you can't read please download the document

Transcript of Clean code for WordPress

Clean code forWordPress plugindevelopment

Mike ToppaPhilly WordCampNovember 5, 2011

Mike Toppa

Director, Web Applications, University of Pennsylvania, School of Medicine Information Services

15 years of experience in web development, project management, and functional managementUniversities: Georgetown, Stanford, Penn

Dot coms: E*Trade, Ask Jeeves

Start-ups: Finexa, Kai's Candy Co

WordPress development for non-profits

Shashin

My plugin for displaying albums, photos, and videos from Picasa, Twitpic, and YouTube(and others coming soon)

I used the new version as a test case for applyingclean code principles to WordPress plugins

What is clean code?

Clean code...

does one thing well - Bjarne Stroustrup

reads like well written prose - Grady Booch

can be read and enhanced by a developer other than its original author - Dave Thomas

always looks like it was written by someone who cares - Michael Feathers

contains no duplication - Ron Jeffries

turns out to be pretty much what you expected - Ward Cunningham

Quotes from Clean Code by Robert Martin

Stroustrup: inventor of C++Booch: IBM chief scientistThomas: founder of Object Technology InternationalFeathers: author of Working Effectively with Legacy CodeJeffries: co-creator of XP developmentCunnigham: inventor of Wikis

That sounds nice, but I have a deadline

I don't have time for clean code!

Do you want your emergency room doctorto rush your surgery?

Do you want your account to rushhis work on your tax return?

Analogy from The Clean Coder

Doctors take the Hippocratic oath and a set of practices to ensure they practice medicine safely

Accounts are expected to do double entry booking, to avoid mistakes

Other professions have standards for what it means to practice that profession safely and responsibly

The software craftsmanshipmovement

The software craftsmanship movement is a part of the Agile community, and is intended to start a conversation about what it means to write code responsibly and cleanly.

You don't have to agree with the ideas, but you do need to engage them

You are responsible for the quality of your code

And to truly go fast, you have to go clean

Why?

The time spent reading codecompared to writing code is 10:1

Code that is hard to read will slow you down

Code that is easy to read frees you to go faster

Clean, modular code is flexible code,
and can adapt to changing requirements

Big ball of mud code can't -it will eventually make you so slow,you'll want to throw it out and start over

Talk about wasted time!

Clean code can be understoodand enhanced by others

Dirty code will drive away potential contributors

Clean code for WordPress

Meaningful names

A web of collaborating objectsThe single responsibility principle (SRP)

The dependency inversion principle (DIP)

Independent architectureThe facade pattern

Unit testing

I will probably need to save unit testing for a session in tomorrow's unconference time

Meaningful names

The name of a variable, function,or class should tell you

why it exists,and what it does

If a name requires a comment,then the name does not reveal its intent

The revelation for me in learning clean code techniques is that code can be expressive. That it really can read like well written prose.

Rather than relying on comments to explain your code, the code can explain itself

Comments become something you have to maintain, and if they become outdated and no longer describe the current behavior of the code, they become dangerous lies

Not good

$d; // elapsed time in days

Good

$elapsedTimeInDays;$daysSinceCreation;$daysSinceModification;

Shashin example

public function setNumericThumbnailSize($requestedSize = 'xsmall') { if (array_key_exists($requestedSize, $this->thumbnailSizesMap)) { $this->numericThumbnailSize = $this->thumbnailSizesMap[$requestedSize]; }

elseif (is_numeric($requestedSize)) { $this->numericThumbnailSize = $requestedSize; }

else { throw New Exception(__('invalid thumbnail size requested', 'shashin')); }

return $this->numericThumbnailSize;}

Take a minute to read this

Even without knowing the class or the properties, it's clear what this method does.

You should use a 21st century IDE, that auto-completes names for you and makes it easy to rename.

I use PHP Storm

The SOLID Principles

Single Responsibility (SRP)

Open-Closed (OCP)

Liskov Substitution (LSP)

Interface Segregation (ISP)

Dependency Inversion (DIP)

Well written object oriented code follows these principles

A web of collaborating objects

The SRP and DIP together drive a composition approach to OO design

From Growing Object Oriented Software, Guided by Tests:

"An object oriented system is a web of collaborating objects... The behavior of the system is an emergent property of the composition of the objects - the choice of objects and how they are connected... Thinking of a system in terms of its dynamic communication structure is a significant mental shift from the static classification that most of us learn when being introduced to objects."

From LosTechies.com

Do one thing, do it well, do it only

For methods, this typically means changing the value of only one variable

If you are passing more than 2 or 3 arguments into a method, you are probably doing more than one thing

For classes, it means having a single conceptual responsibility

You want high cohesiveness in a class

This is the opposite of how most WordPress plugins are written

My Shashin plugin consists of 44 classes, each of which has a meaningful name, follows the SRP, and does one thing

PicasaPhotoDisplayerSettingsMenuYouTubeSynchronizerUninstalletc.

class ShashinInstall { // ... public function run() { $this->createAlbumTable(); $this->verifyAlbumTable(); $this->createPhotoTable(); $this->verifyPhotoTable(); $this->updateSettings(); return true; }

// ...}

Shashin example

The whole class is about 100 lines

The run method calls the other methods (this is a simple use of the command pattern)

It reads like a table of contents

Breaking the functionality down into methods that do one thing makes the code easier to read and to unit test

Shashin's capabilities are shaped by how these objects are wired together, through implementation of the DIP

From LosTechies.com

It's common to see code that hard-wires together all the parts, when those connections could be made more flexible and extensible

Nave model of a button and lamp

Button

+ poll()Lamp

+ turnOn()+ turnOff()

class Button {private $lamp;

public function __construct(Lamp $lamp) {$this->lamp = $lamp;}

public function poll() {if (/* some condition */) {$this->lamp->turnOn();}}}

Example from Agile Software Development

This solution violates the DIP

Button depends directly on LampChanges to Lamp may require changes to Button

Button is not reusableIt can't control, for example, a Motor

Dependency inversion applied

Button

+ poll()

SwitchableDevice

+ turnOn()+ turnOff()

Lamp

This is the Abstract Server pattern

class Lamp implements SwitchableDevice {public function turnOn() {// code}

public function turnOff() {// code}}

class Button {private $switchableDevice;

public function __construct(SwitchableDevice $switchableDevice) {$this->switchableDevice = $switchableDevice;}

public function poll() {if (/* some condition */) {$this->switchableDevice->turnOn();}}}

What it means

Neither Button nor Lamp own the interface

Buttons can now control any device that implements SwitchableDevice

Lamps and other SwitchableDevices can now be controlled by any object that accepts a SwitchableDevice

I'll provide a Shashin example after introducing one more concept

Independent Architecture

Software architectures are structures thatsupport the use cases of the system...

Frameworks are tools to be used, not architectures to be conformed to

- Bob Martin

http://blog.8thlight.com/uncle-bob/2011/09/30/Screaming-Architecture.html

Looking at the Shashin files and code, it screams photo management and display!

It does not scream WordPress plugin

The fact that it is a WordPress plugin is incidental to its design

This makes it possible to do unit testing of your plugins, and makes them potentially usable outside of WordPress

In my plugins, WordPress custom functionsare moved behind a facade

Facade pattern for my plugins

ShashinFunctionsfacadeinterface

Some otherfacadeimplementationWordPressfacadeimplementationYet anotherfacadeimplementation

A different implementation of the facade would allow Shashin to work outside of WordPress, without touching Shashin code

interface ToppaFunctionsFacade { // ... public function enqueueStylesheet($handle, $relativePath, $dependencies = false, $version = null, $media = null);}

class ToppaFunctionsFacadeWp implements ToppaFunctionsFacade { // public function enqueueStylesheet($handle, $relativePath, $dependencies = false, $version = null, $media = null) { return wp_enqueue_style($handle, $relativePath, $dependencies, $version, $media); }}

// use in Shashin // ... public function setFunctionsFacade(ToppaFunctionsFacade $functionsFacade) { $this->functionsFacade = $functionsFacade; return $this->functionsFacade; }

// ... $this->functionsFacade->enqueueStylesheet('shashinAdminStyle', $cssUrl, false, $this->version);

Shashin example

But I decided to not use a facade forWordPress' hooks and filters

At the highest level, other CMS' or frameworks are not going to wire Shashin together to same way as WordPress.

Also, since WordPress' hooks and filters return nothing, they are hard to mock for tests

I need a better understanding of what other environments are like before I can design an interface with the appropriate flexibility

I isolated the WordPress hooks and filtersin a single method

This isolates the impact of changes that might be needed in the future, since I don't yet have a specific use case for Shashin outside of WordPress, but I want to be ready when the need comes

public function run() { add_action('admin_menu', array($this, 'initToolsMenu')); add_action('admin_menu', array($this, 'initSettingsMenu')); add_action('template_redirect', array($this, 'displayPublicHeadTags')); add_shortcode('shashin', array($this, 'handleShortcode')); add_action('wp_ajax_nopriv_displayAlbumPhotos', array($this, 'ajaxDisplayAlbumPhotos')); add_action('wp_ajax_displayAlbumPhotos', array($this, 'ajaxDisplayAlbumPhotos')); add_action('media_buttons', array($this, 'addMediaButton'), 30); add_action('media_upload_shashin_photos', array($this, 'initPhotoMediaMenu')); add_action('media_upload_shashin_albums', array($this, 'initAlbumMediaMenu')); add_action('wp_ajax_shashinGetPhotosForMediaMenu', array($this, 'ajaxGetPhotosForMediaMenu')); add_action('shashinSync', array($this, 'runScheduledSync')); add_action('widgets_init', array($this, 'registerWidget')); add_action('admin_head', array($this, 'displayPluginPageUpgradeNag')); }

// ... public function runScheduledSync() { $adminContainer = new Admin_ShashinContainer($this->autoLoader); $scheduledSynchronizer = $adminContainer->getScheduledSynchronizer(); $scheduledSynchronizer->run(); }

ShashinWp

Each function that is called instantiates the necessary objects for that hook.

This approach is efficient. Shashin is a big plugin with a lot of files, but the only code loaded at runtime is the code needed

Unit testing and TDD

Having an automated test suite can alert you to bugs before your customers find them

TDD drives good design, but of all the clean code practices, is the hardest to learn how to do well

The test frameworks available for PHP are

PHPUnit (the standard)

and

SimpleTest

Although PHPUnit is the standard, I chose to create a plugin for using SimpleTest with WordPress because:

It's designed for web use and is very easy to subclass and package with a plugin

It's approach to mock objects is easier to understand and use than PHPUnit's

SimpleTest for WordPress

For my plugin, you create your tests, then in a shortcode give the path to the tests, and SimpleTest will run them.

Since it's running within WordPress, you can also use it for integration testing (that is, calling actual WordPress functions will work)

Shashin example

This is one of the tests for the method setTableCaptionTag(), in the class ShashinLayoutManager

Mock::generate('Lib_ShashinSettings');Mock::generate('Lib_ShashinAlbumPhotosCollection');

class UnitPublic_ShashinLayoutManager extends UnitTestCase {

// public function testSetTableCaptionTagWithFewerPhotosThanMaxPerPage() { $layoutManager = new Public_ShashinLayoutManager(); $collection = new MockLib_ShashinAlbumPhotosCollection(); $collection->setReturnValue('getCount', 9); $layoutManager->setDataObjectCollection($collection); $settings = new MockLib_ShashinSettings(); $settings->setReturnValue('getPhotoLimit',10); $this->layoutManager->setSettings($settings); $this->assertNull($this->layoutManager->setTableCaptionTag()); }}

But don't overdo it:
avoid needless complexity

The complexity of having 44 classes in Shashin is justified by its need for flexibility

You can support a new viewer or a new photo service simply by creating a new subclass. This is much more maintainable and extensible than a single huge file with deeply nested conditionals and unclear dependencies

But if I knew I would never need that kind of flexibility, there would be no justification for creating these layers of abstraction they would just be needless complexity

Contact

[email protected]@mtoppa