Clean code for WordPress
-
Upload
mtoppa -
Category
Technology
-
view
3.646 -
download
0
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