Download - implements Hello_Dolly
implementsHello_DollyImageCredit:KyonFlickr•https://flic.kr/p/8x4bVQ
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
JonathanBrinley
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
"Hello,Dolly!"
ImageCredit:ClassicFilmonFlickr•https://flic.kr/p/KD7WWE
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
"Hello,Dolly!"
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
hello.php/***@packageHello_Dolly*@version1.6*//*PluginName:HelloDollyPluginURI:http://wordpress.org/plugins/hello-dolly/Description:Thisisnotjustaplugin,itsymbolizesthehopeandenthusiasmofanentiregenerationsummedupintwowordssungmostfamouslybyLouisArmstrong:Hello,Dolly.Whenactivatedyouwillrandomlyseealyricfrom<cite>Hello,Dolly</cite>intheupperrightofyouradminscreenoneverypage.Author:MattMullenwegVersion:1.6AuthorURI:http://ma.tt/*/
functionhello_dolly_get_lyric(){ /**ThesearethelyricstoHelloDolly*/ $lyrics="Hello,DollyWell,hello,DollyIt'ssonicetohaveyoubackwhereyoubelongYou'relookin'swell,DollyIcantell,DollyYou'restillglowin',you'restillcrowin'You'restillgoin'strongWefeeltheroomswayin'Whiletheband'splayin'
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
Refactor!
github.com/flightless/implements-hello-dollyImageCredit:ClassicFilmonFlickr•https://flic.kr/p/LxazKW
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
implements-hello-dolly.php/*PluginName:implementsHello_DollyDescription:Westartwitheveryone’sfavoriteWordPressplugin,“Hello,Dolly”.Withadramaticwaveofourhands,wespeakthemagicalword:“Refactor!”Let’sseehowfarwecango.Author:JonathanBrinleyVersion:2.0Contributors:MattMullenweg*/
namespaceHello_Dolly;
//Startthepluginadd_action('plugins_loaded',function(){ require_once__DIR__.'/vendor/autoload.php'; Hello_Dolly_Plugin::init(); do_action('hello_dolly/init');},1,0);
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
Composer
AutoloadingLibraries
getcomposer.org
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
Composercomposerinit
{"name":"flightless/implements-hello-dolly","description":"Westartwitheveryone’sfavoriteWordPressplugin,“Hello,Dolly”.Withadramaticwaveofourhands,wespeakthemagicalword:“Refactor!”Let’sseehowfarwecango.""type":"wordpress-plugin","license":"GPL-2.0","authors":[{"name":"JonathanBrinley","email":"[email protected]"}]}
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
composer.json{"name":"flightless/implements-hello-dolly","description":"Westartwitheveryone’sfavoriteWordPressplugin,“Hello,Dolly”.Withadramaticwaveofourhands,wespeakthemagicalword:“Refactor!”Let’sseehowfarwecango.""type":"wordpress-plugin","license":"GPL-2.0","authors":[{"name":"JonathanBrinley","email":"[email protected]"}],"autoload":{"psr-4":{"Hello_Dolly\\":"src/"}}}
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
Codeceptioncomposerrequire--devlucatume/wp-browser
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
Codeception
composerinstall--no-dev
{"name":"flightless/implements-hello-dolly",/*...*/"autoload":{"psr-4":{"Hello_Dolly\\":"src/"}},"require-dev":{"lucatume/wp-browser":"^1.16"}}
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
WPLoadervendor/bin/wpceptbootstrap
https://github.com/lucatume/wp-browser
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
tests/integration/Hello_Dolly/Hello_Dolly_Plugin_Test.phpnamespaceHello_Dolly;useCodeception\TestCase\WPTestCase;
classHello_Dolly_Plugin_TestextendsWPTestCase{ publicfunctiontest_get_instance(){ $instance=Hello_Dolly_Plugin::instance(); $this->assertInstanceOf('Hello_Dolly\\Hello_Dolly_Plugin',$instance); }}
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
implements-hello-dolly.phpnamespaceHello_Dolly;
//Startthepluginadd_action('plugins_loaded',function(){ require_once__DIR__.'/vendor/autoload.php'; Hello_Dolly_Plugin::init(); do_action('hello_dolly/init');},1,0);
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
src/Hello_Dolly_Plugin.phpnamespaceHello_Dolly;
/***InitializestheHello,Dollyplugin*/classHello_Dolly_Plugin{ /**@varstatic*/ privatestatic$instance;
/** *Initializetheplugin */ publicstaticfunctioninit(){ self::instance(); }
/** *Gettheglobalinstanceoftheclass *@returnstatic */ publicstaticfunctioninstance(){ if(empty(static::$instance)){ static::$instance=newstatic(); } returnstatic::$instance;
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
FirstFivePrinciplesSingleresponsibilityprincipleOpen/closedprincipleLiskovsubstitutionprincipleInterfacesegregationprincipleDependencyinversionprinciple
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
SingleresponsibilityprincipleTurnastringoflyricsintoanarrayGetarandomlinefromthelyricsarrayTexturizealineoflyricsWraplyricsinaparagraphtagBuildlanguage-appropriateCSSrulesPrintthelyricstothebrowserPrintthestylerulestothebrowserHookintoWordPress
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
Lyrics\Lyric_CollectionResponsibility
Fetchasinglelinefromanarrayoflyrics
Whywoulditchange?
Ifthealgorithmtofetchalinechanged
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
tests/integration/Hello_Dolly/Lyrics/Lyric_Collection_Test.phpnamespaceHello_Dolly\Lyrics;useCodeception\TestCase\WPTestCase;
classLyric_Collection_TestextendsWPTestCase{ publicfunctiontest_get_lyric(){ $lyrics=[]; for($i=0;$i<10;$i++){ $lyrics[]=rand_str(mt_rand(0,32)).''.rand_str(mt_rand(0,32)).''.rand_str(mt_rand(0,32)); }
$collection=newLyric_Collection($lyrics); for($i=0;$i<20;$i++){ $this->assertContains($collection->get_lyric(),$lyrics); } }}
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
Lyrics\Lyric_CollectionnamespaceHello_Dolly\Lyrics;
classLyric_CollectionimplementsLyric_Collection_Interface{ /**@varstring[]*/ protected$lyrics;
/**@varint*/ protected$count=0;
/** *Lyric_Collectionconstructor. * *@paramarray$lyricsAnarrayofsingle-linestrings */ publicfunction__construct(array$lyrics){ $this->lyrics=array_values($lyrics); $this->count=count($this->lyrics); }
publicfunctionget_lyric(){ return$this->get_random_lyric(); }
/** *Getarandomitemfromthelistoflyrics
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
Lyrics\Lyric_Collection_InterfacenamespaceHello_Dolly\Lyrics;
/***InterfaceLyric_Collection_Interface**Implementorswillreturnasinglelineoflyrics*/interfaceLyric_Collection_Interface{ /** *@returnstringAsinglelineoflyrics */ publicfunctionget_lyric();}
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
Lyrics\Lyric_Collection_FactoryResponsibility
TransformastringoflyricsintoaLyric_CollectionWhywoulditchange?
Ifthereturnvalueshouldbeadifferentclass
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
tests/integration/Hello_Dolly/Lyrics/Lyric_Collection_Factory_Test.php
namespaceHello_Dolly\Lyrics;useCodeception\TestCase\WPTestCase;
classLyric_Collection_Factory_TestextendsWPTestCase{ publicfunctiontest_create_collection(){ $lyrics=[]; for($i=0;$i<10;$i++){ $lyrics[]=rand_str(mt_rand(0,32)).''.rand_str(mt_rand(0,32)).''.rand_str(mt_rand(0,32)); } $string=implode("\n",$lyrics); $factory=newLyric_Collection_Factory(); $collection=$factory->create_collection($string);
$this->assertInstanceOf('Hello_Dolly\\Lyrics\\Lyric_Collection',$collection); }}
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
Lyrics\Lyric_Collection_FactorynamespaceHello_Dolly\Lyrics;
/***ClassLyric_Collection_Factory**CreatesaLyric_Collectioninstancefrom*astringoflyrics.*/classLyric_Collection_FactoryimplementsLyric_Collection_Factory_Interface{ /** *@paramstring$lyrics *@returnLyric_Collection */ publicfunctioncreate_collection($lyrics){ $lyrics=explode("\n",$lyrics); returnnewLyric_Collection($lyrics); }}
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
Lyrics\Lyric_Collection_Factory_InterfacenamespaceHello_Dolly\Lyrics;
/***InterfaceLyric_Collection_Factory_Interface**CreatesaLyric_Collection_Interfacefroma*multi-linestringoflyrics.*/interfaceLyric_Collection_Factory_Interface{ /** *@paramstring$lyricsAmulti-linestringoflyrics *@returnLyric_Collection_Interface */ publicfunctioncreate_collection($lyrics);}
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
FormattingResponsibility
Applywptexturize()toastringWrapastringinaparagraphtagwithanID
Whywoulditchange?
Theformattingalgorithmchanges
TheHTMLelementchanges
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
Decorator
format(string)String_Formatterformat(string)
String_Formatter
format(string)String_Formatter_Interfaceformat(string)
String_Formatter_Interface
html_id__construct(formatter,html_id)decorate(string)
Paragraph_Taghtml_id__construct(formatter,html_id)decorate(string)
Paragraph_Tagdecorate(string)
WP_Texturize_Formatterdecorate(string)
WP_Texturize_Formatter
formatter__construct(formatter)format(string)decorate(string)
String_Formatter_Decoratorformatter__construct(formatter)format(string)decorate(string)
String_Formatter_Decorator
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
Formatting\String_Formatter_InterfacenamespaceHello_Dolly\Formatting;
/***InterfaceString_Formatter_Interface**Implementorswillformatandreturnastring.*/interfaceString_Formatter_Interface{ /** *@paramstring$stringThestringtobeformatted *@returnstringTheformattedstring */ publicfunctionformat($string);}
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
Formatting\String_FormatternamespaceHello_Dolly\Formatting;useCodeception\TestCase\WPTestCase;
classString_Formatter_TestextendsWPTestCase{ publicfunctiontest_passthrough(){ $string=rand_str(); $passthrough=newString_Formatter();
$this->assertEquals($string,$passthrough->format($string)); }}
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
Formatting\String_FormatternamespaceHello_Dolly\Formatting;
/***ClassString_Formatter**Apassthroughformatterthatreturnsthegivenstring*/classString_FormatterimplementsString_Formatter_Interface{ publicfunctionformat($string){ return$string; }}
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
Formatting\String_Formatter_DecoratornamespaceHello_Dolly\Formatting;
/***ClassString_Formatter_Decorator**Baseclassforstringdecorators.*/abstractclassString_Formatter_DecoratorimplementsString_Formatter_Interface{ /**@varString_Formatter_Interface*/ protected$formatter; publicfunction__construct(String_Formatter_Interface$formatter){ $this->formatter=$formatter; }
publicfunctionformat($string){ $string=$this->formatter->format($string); return$this->decorate($string); }
abstractprotectedfunctiondecorate($string);}
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
Formatting\WP_Texturize_FormatternamespaceHello_Dolly\Formatting;useCodeception\TestCase\WPTestCase;
classWP_Texturize_Formatter_TestextendsWPTestCase{ publicfunctiontest_texturization(){ $string="You'relookin'swell,Dolly"; $texturized_string=wptexturize($string); $this->assertNotEquals($string,$texturized_string);
$formatter=newWP_Texturize_Formatter(newString_Formatter()); $this->assertEquals($texturized_string,$formatter->format($string)); }}
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
Formatting\WP_Texturize_FormatternamespaceHello_Dolly\Formatting;
/***ClassWP_Texturize_Formatter**Formatsastringwithwptexturize()*@seewptexturize()*/classWP_Texturize_FormatterextendsString_Formatter_Decorator{ protectedfunctiondecorate($string){ returnwptexturize($string); }}
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
Formatting\Paragraph_TagnamespaceHello_Dolly\Formatting;useCodeception\TestCase\WPTestCase;
classParagraph_Tag_TestextendsWPTestCase{ publicfunctiontest_paragraph_tag(){ $string=rand_str(); $id=rand_str();
$formatter=newParagraph_Tag(newString_Formatter(),$id);
$expected=sprintf('<pid="%s">%s</p>',$id,$string); $this->assertEquals($expected,$formatter->format($string)); }
publicfunctiontest_escaped_id(){ $string=rand_str(); $id=rand_str(8).''.rand_str(8); $escaped_id=sanitize_html_class($id); $this->assertNotEquals($id,$escaped_id);
$formatter=newParagraph_Tag(newString_Formatter(),$id);
$expected=sprintf('<pid="%s">%s</p>',$escaped_id,$string); $this->assertEquals($expected,$formatter->format($string)); }
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
Formatting\Paragraph_TagnamespaceHello_Dolly\Formatting;
/***ClassParagraph_Tag**WrapsastringinaparagraphtagwithanID*/classParagraph_TagextendsString_Formatter_Decorator{ /**@varstring*/ protected$html_id;
/** *Paragraph_Tagconstructor. * *@paramString_Formatter_Interface$formatter *@param$html_id */ publicfunction__construct(String_Formatter_Interface$formatter,$html_id){ parent::__construct($formatter); $this->html_id=$html_id; }
protectedfunctiondecorate($string){ returnsprintf('<pid="%s">%s</p>',sanitize_html_class($this->html_id),$string); }
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
<pid="dolly">You’relookin’swell,Dolly</p>
$formatter=newParagraph_Tag(newWP_Texturize_Formatter(newString_Formatter()),'dolly');return$formatter->format("You'relookin'swell,Dolly");
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
Styles\Style_Rules_InterfacenamespaceHello_Dolly\Styles;
/***InterfaceStyle_Rules_Interface**GeneratesalistofCSSproperties*/interfaceStyle_Rules_Interface{
/** *@returnstring */ publicfunctionget_styles();}
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
Styles\Directional_Style_RulesnamespaceHello_Dolly\Styles;
/***ClassDirectional_Style_Rules**Generatesstylesforthegiventextdirection*/classDirectional_Style_RulesimplementsStyle_Rules_Interface{ protected$side;
/** *Directional_Style_Rulesconstructor. * *@paramstring$sideThesidetextfloatsto.'left'or'right' */ publicfunction__construct($side){ if(empty($side)){ thrownew\InvalidArgumentException(__('$this->sidemustbesettoanon-emptyvalue')); } $this->side=$side; }
publicfunctionget_styles(){ return" float:{$this->side};
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
Styles\Style_Printer_InterfacenamespaceHello_Dolly\Styles;
/***InterfaceStyle_Printer_Interface**PrintsCSSstyles*/interfaceStyle_Printer_Interface{ /** *@paramStyle_Rules_Interface$styles *@returnvoid */ publicfunctionprint_styles(Style_Rules_Interface$styles);}
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
Styles\Style_PrinternamespaceHello_Dolly\Styles;
/***ClassStyle_Printer**PrintsthegivenstylerulesfortheelementwiththegivenID*/classStyle_PrinterimplementsStyle_Printer_Interface{ protected$html_id;
/** *Style_Printerconstructor. * *@paramstring$html_idTheIDofthetargetedDOMelement */ publicfunction__construct($html_id){ $this->html_id=$html_id; }
/** *@paramStyle_Rules_Interface$styles *@returnvoid */ publicfunctionprint_styles(Style_Rules_Interface$styles){ $rules=$styles->get_styles();
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
src/Hello_Dolly_Plugin.phpprivatefunctionhooks(){ add_action('admin_head',[$this,'print_admin_css']); add_action('admin_notices',[$this,'print_admin_notice']);}
publicfunctionprint_admin_css(){ $style_rules=$this->get_style_rules(); $printer=$this->get_style_printer(); $printer->print_styles($style_rules);}
publicfunctionprint_admin_notice(){ $lyrics=$this->get_lyric_collection(); $printer=$this->get_lyric_printer(); $printer->render($lyrics->get_lyric());}
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
src/Hello_Dolly_Plugin.php/***@returnStyle_Rules_Interface*/privatefunctionget_style_rules(){ if(is_rtl()){ $style_rules=$this->container['style_rules.rtl']; }else{ $style_rules=$this->container['style_rules.ltr']; } returnapply_filters('hello_dolly/style_rules',$style_rules);}
/***@returnStyle_Printer_Interface*/privatefunctionget_style_printer(){ $printer=$this->container['style_printer']; returnapply_filters('hello_dolly/style_printer',$printer);}
/***@returnLyric_Collection_Interface*/privatefunctionget_lyric_collection(){ $collection=$this->container['lyrics.collection'];
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
src/Hello_Dolly_Plugin.php/***Initializetheplugin**@returnvoid*/publicstaticfunctioninit(Container$container){ $instance=self::instance(); $instance->container=$container; $instance->hooks();}
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
DependencyInjectionContainerAura.DiPHP-DIPimpleSymphony\DependencyInjectionZend\Di
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
Pimplecomposerrequirepimple/pimple
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
implements-hello-dolly.phpnamespaceHello_Dolly;usePimple\Container;
//Startthepluginadd_action('plugins_loaded',function(){ require_once__DIR__.'/vendor/autoload.php'; $container=newContainer(); $container->register(newService_Provider()); Hello_Dolly_Plugin::init($container); do_action('hello_dolly/init',Hello_Dolly_Plugin::instance(),$container);},1,0);
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
src/Service_Provider.phpnamespaceHello_Dolly;
useHello_Dolly\Formatting\Paragraph_Tag;useHello_Dolly\Formatting\String_Formatter;useHello_Dolly\Formatting\WP_Texturize_Formatter;useHello_Dolly\Lyrics\Lyric_Collection_Factory;useHello_Dolly\Printing\Formatted_Printer;useHello_Dolly\Styles\Directional_Style_Rules;useHello_Dolly\Styles\Style_Printer;usePimple\Container;usePimple\ServiceProviderInterface;
classService_ProviderimplementsServiceProviderInterface{ publicfunctionregister(Container$container){ $container['html_id']='dolly';
$container['lyrics']="Hello,DollyWell,hello,DollyIt'ssonicetohaveyoubackwhereyoubelongYou'relookin'swell,DollyIcantell,DollyYou'restillglowin',you'restillcrowin'You'restillgoin'strong
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
implements-hello-dolly.phpnamespaceHello_Dolly;usePimple\Container;
//Startthepluginadd_action('plugins_loaded',function(){ require_once__DIR__.'/vendor/autoload.php'; $container=newContainer(); $container->register(newService_Provider()); Hello_Dolly_Plugin::init($container); do_action('hello_dolly/init',Hello_Dolly_Plugin::instance(),$container);},1,0);
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
ResourcesimplementsHello_Dollygithub.com/flightless/implements-hello-dolly
ThePrinciplesofOOD/RobertC.Martinbutunclebob.com/ArticleS.UncleBob.PrinciplesOfOod
InversionofControlContainersandtheDependencyInjectionpattern/MartinFowlermartinfowler.com/articles/injection.html
WPBrowser/LucaTumedeigithub.com/lucatume/wp-browsertheaveragedev.com/tag/wp-browser/
ImageCredit:NewYorkSundayNews
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016
JonathanBrinley@jbrinley•[email protected]
JonathanBrinley• •#wcorl•Slides:@jbrinley flightless.us/wcorl2016