Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Why Modern PHP is Awesome And How You Can Use It Today

Why Modern PHP is Awesome And How You Can Use It Today

From PeersConf 2014, a conference primarily for PHP developers working with ExpressionEngine, Craft, Statamic, and CodeIgniter.

The PHP language and its community have seen incredible growth in the last few years. It can be hard to keep up with the pace of new development, and harder to keep our sites up-to-date. But these new packages, practices, and SAASes have amazing potential to improve our sites' flexibility, power, ease of development, and longevity. This isn't just for brand-new frameworks like Laravel, either--CMSes like Craft and PyroCMS have them baked in, and even ExpressionEngine and Codeigniter can be modified to work with modern PHP. Learn about Composer, Packagist, SOLID, SAASes, exceptions, PSRs, and how these and many more tools and practices will transform your experience of PHP.

Matt Stauffer

April 25, 2014
Tweet

More Decks by Matt Stauffer

Other Decks in Programming

Transcript

  1. My history Graphic Design Frontend Hacky PHP & Wordpress CodeIgniter

    Expression Engine Rails Laravel Left for 5 years
  2. My history Graphic Design Frontend Hacky PHP & Wordpress CodeIgniter

    Expression Engine Rails Laravel General Good Developer-y Stuff Left for 5 years
  3. Disclaimers: 1. You might already know this 2. This may

    be totally overwhelming 3. Easiest in modern environments
  4. Disclaimers: 1. You might already know this 2. This may

    be totally overwhelming 3. Easiest in modern environments 4. One size fits some
  5. <?php require_once("common.inc"); $page_slug = getPageSlug($_GET); $page_title = getPageTitle($page_slug); ! include_once("templates/header.inc");

    ! echo getHeaderNav($page_slug); ! // ... about.php include_once( "templates/about.inc" ); ! include_once( "templates/footer.inc" ); Procedural PHP
  6. <?php require_once("common.inc"); $page_slug = getPageSlug($_GET); $page_title = getPageTitle($page_slug); ! include_once("templates/header.inc");

    ! echo getHeaderNav($page_slug); ! // ... about.php include_once( "templates/about.inc" ); ! include_once( "templates/footer.inc" ); portfolio.php $portfolio_items = array( array( 'title' => 'Chuck E Cheese', 'screenshot' => 'images/portfolio/ chuck01.jpg' Procedural PHP
  7. <?php require_once("common.inc"); $page_slug = getPageSlug($_GET); $page_title = getPageTitle($page_slug); ! include_once("templates/header.inc");

    ! echo getHeaderNav($page_slug); ! // ... about.php include_once( "templates/about.inc" ); ! include_once( "templates/footer.inc" ); portfolio.php $portfolio_items = array( array( 'title' => 'Chuck E Cheese', 'screenshot' => 'images/portfolio/ chuck01.jpg' contact-us.ph if(isset($_POST)) $results = va if ($results include_o } else { $errors = } Procedural PHP
  8. <?php class Portfolio_item { public $id; public $title; public $url;

    protected $cache_images; ! public function __construct(array $properties) { $this->hydrate($properties); } ! public function getRelatedImages() { if ($this->cache_images === null) { $this->cacheRelatedImages(); } ! return $this->cache_images; } ! protected function hydrate(array $properties) Object-Oriented PHP
  9. <?php class Portfolio_item { public $id; public $title; public $url;

    protected $cache_images; ! public function __construct(array $properties) { $this->hydrate($properties); } ! public function getRelatedImages() { if ($this->cache_images === null) { $this->cacheRelatedImages(); } ! return $this->cache_images; } ! protected function hydrate(array $properties) Name Object-Oriented PHP
  10. <?php class Portfolio_item { public $id; public $title; public $url;

    protected $cache_images; ! public function __construct(array $properties) { $this->hydrate($properties); } ! public function getRelatedImages() { if ($this->cache_images === null) { $this->cacheRelatedImages(); } ! return $this->cache_images; } ! protected function hydrate(array $properties) Name Properties Object-Oriented PHP
  11. <?php class Portfolio_item { public $id; public $title; public $url;

    protected $cache_images; ! public function __construct(array $properties) { $this->hydrate($properties); } ! public function getRelatedImages() { if ($this->cache_images === null) { $this->cacheRelatedImages(); } ! return $this->cache_images; } ! protected function hydrate(array $properties) Name Properties Constructor Object-Oriented PHP
  12. <?php class Portfolio_item { public $id; public $title; public $url;

    protected $cache_images; ! public function __construct(array $properties) { $this->hydrate($properties); } ! public function getRelatedImages() { if ($this->cache_images === null) { $this->cacheRelatedImages(); } ! return $this->cache_images; } ! protected function hydrate(array $properties) Name Properties Constructor Public method Object-Oriented PHP
  13. <?php class Portfolio_item { public $id; public $title; public $url;

    protected $cache_images; ! public function __construct(array $properties) { $this->hydrate($properties); } ! public function getRelatedImages() { if ($this->cache_images === null) { $this->cacheRelatedImages(); } ! return $this->cache_images; } ! protected function hydrate(array $properties) Name Properties Constructor Public method Protected method Object-Oriented PHP
  14. ! ! ! ! ! <!-- View file: --> !

    <h1><?= $item->title; ?></h1> ! <h2>Images</h2> ! <ul class="slider"> <?php foreach ($item->getRelatedImages() as $image): ?> <li><img src="<?= $image->url; ?>" alt="<?= $image->alt; ?>"></li> <?php endforeach; ?> </ul>
  15. PHP is an OOP late bloomer 2007 2015 2011 2003

    1999 1995 PHP 5.3 (true OOP) PHP released 1995
  16. PHP is an OOP late bloomer 2007 2015 2011 2003

    1999 1995 PHP 5.3 (true OOP) PHP released 1995 PHP 5.2 EOL’ed
  17. PHP is an OOP late bloomer 2007 2015 2011 2003

    1999 1995 PHP 5.3 (true OOP) PHP released 1995 5.3 50% adoption PHP 5.2 EOL’ed
  18. What OOP isn’t Taking all of your old DB- related

    functions & bundling them into a "model"
  19. SOLID 1. Single Responsibility 2. Open-Closed 3. Liskov Substitution 4.

    Interface Segregation 5. Dependency Inversion
  20. * @param array $contact Contact * @param array $options Options

    * @return string Full name */ function returnContactFullName( array $contact, $options = array() { // Tack middle name onto first if ($contact['contact_mname'] != '') { $contact['contact_fname'] .= ' ' . $contact['contact_mname']; } if ($contact['contact_sp_mname'] != '') { $contact['contact_sp_fname'] .= ' '.$contact['contact_sp_mname' } ! if ( ! empty($contact['contact_sp_fname']) || ( ! empty($contact[ empty($contact['contact_sp_title']))) { // Contact has a spouse. // Prep title $contact_title = $spouse_title = NULL; ! if ( (empty($contact['contact_title']) && empty($contact['contact_ in_array('no_title', $options) ) { // No titles. Go without. } elseif (empty($contact['contact_sp_title'])) { // Title for contact but not spouse. Only can handle if Mr. if ($contact['contact_title'] == 'Mr' || $contact['contact_ti $contact['contact_sp_title'] = str_replace('Mr', 'Mrs', $co $contact_title = $contact['contact_title'];
  21. * * @todo Get this out of the helper *

    @param array $contact Contact * @param array $options Options * @return string Full name */ function returnContactFullName( array $contact, $options = array() ) { // Tack middle name onto first if ($contact['contact_mname'] != '') { $contact['contact_fname'] .= ' ' . $contact['contact_mname']; } if ($contact['contact_sp_mname'] != '') { $contact['contact_sp_fname'] .= ' '.$contact['contact_sp_mname']; } ! if ( ! empty($contact['contact_sp_fname']) || ( ! empty($contact['contact_title']) && ! empty($contact['contact_sp_title']))) { // Contact has a spouse. // Prep title $contact_title = $spouse_title = NULL; ! if ( (empty($contact['contact_title']) && empty($contact['contact_sp_title'])) || in_array('no_title', $options) ) { // No titles. Go without. } elseif (empty($contact['contact_sp_title'])) { // Title for contact but not spouse. Only can handle if Mr. if ($contact['contact_title'] == 'Mr' || $contact['contact_title'] == 'Mr.') { $contact['contact_sp_title'] = str_replace('Mr', 'Mrs', $contact['contact_title']) $contact_title = $contact['contact_title']; Extract refactor
  22. * * @todo Get this out of the helper *

    @param array $contact Contact * @param array $options Options * @return string Full name */ function returnContactFullName( array $contact, $options = array() ) { // Tack middle name onto first if ($contact['contact_mname'] != '') { $contact['contact_fname'] .= ' ' . $contact['contact_mname']; } if ($contact['contact_sp_mname'] != '') { $contact['contact_sp_fname'] .= ' '.$contact['contact_sp_mname']; } ! if ( ! empty($contact['contact_sp_fname']) || ( ! empty($contact['contact_title']) && ! empty($contact['contact_sp_title']))) { // Contact has a spouse. // Prep title $contact_title = $spouse_title = NULL; ! if ( (empty($contact['contact_title']) && empty($contact['contact_sp_title'])) || in_array('no_title', $options) ) { // No titles. Go without. } elseif (empty($contact['contact_sp_title'])) { // Title for contact but not spouse. Only can handle if Mr. if ($contact['contact_title'] == 'Mr' || $contact['contact_title'] == 'Mr.') { $contact['contact_sp_title'] = str_replace('Mr', 'Mrs', $contact['contact_title']) $contact_title = $contact['contact_title']; Extract refactor $this->appendMiddleNames();
  23. * * @todo Get this out of the helper *

    @param array $contact Contact * @param array $options Options * @return string Full name */ function returnContactFullName( array $contact, $options = array() ) { // Tack middle name onto first if ($contact['contact_mname'] != '') { $contact['contact_fname'] .= ' ' . $contact['contact_mname']; } if ($contact['contact_sp_mname'] != '') { $contact['contact_sp_fname'] .= ' '.$contact['contact_sp_mname']; } ! if ( ! empty($contact['contact_sp_fname']) || ( ! empty($contact['contact_title']) && ! empty($contact['contact_sp_title']))) { // Contact has a spouse. // Prep title $contact_title = $spouse_title = NULL; ! if ( (empty($contact['contact_title']) && empty($contact['contact_sp_title'])) || in_array('no_title', $options) ) { // No titles. Go without. } elseif (empty($contact['contact_sp_title'])) { // Title for contact but not spouse. Only can handle if Mr. if ($contact['contact_title'] == 'Mr' || $contact['contact_title'] == 'Mr.') { $contact['contact_sp_title'] = str_replace('Mr', 'Mrs', $contact['contact_title']) $contact_title = $contact['contact_title']; Extract refactor if ($this->hasSpouse()) { $this->appendMiddleNames();
  24. * * @todo Get this out of the helper *

    @param array $contact Contact * @param array $options Options * @return string Full name */ function returnContactFullName( array $contact, $options = array() ) { // Tack middle name onto first if ($contact['contact_mname'] != '') { $contact['contact_fname'] .= ' ' . $contact['contact_mname']; } if ($contact['contact_sp_mname'] != '') { $contact['contact_sp_fname'] .= ' '.$contact['contact_sp_mname']; } ! if ( ! empty($contact['contact_sp_fname']) || ( ! empty($contact['contact_title']) && ! empty($contact['contact_sp_title']))) { // Contact has a spouse. // Prep title $contact_title = $spouse_title = NULL; ! if ( (empty($contact['contact_title']) && empty($contact['contact_sp_title'])) || in_array('no_title', $options) ) { // No titles. Go without. } elseif (empty($contact['contact_sp_title'])) { // Title for contact but not spouse. Only can handle if Mr. if ($contact['contact_title'] == 'Mr' || $contact['contact_title'] == 'Mr.') { $contact['contact_sp_title'] = str_replace('Mr', 'Mrs', $contact['contact_title']) $contact_title = $contact['contact_title']; Extract refactor if ($this->hasSpouse()) { if ( ! $this->hasTitles()) { $this->appendMiddleNames();
  25. * * @todo Get this out of the helper *

    @param array $contact Contact * @param array $options Options * @return string Full name */ function returnContactFullName( array $contact, $options = array() ) { // Tack middle name onto first if ($contact['contact_mname'] != '') { $contact['contact_fname'] .= ' ' . $contact['contact_mname']; } if ($contact['contact_sp_mname'] != '') { $contact['contact_sp_fname'] .= ' '.$contact['contact_sp_mname']; } ! if ( ! empty($contact['contact_sp_fname']) || ( ! empty($contact['contact_title']) && ! empty($contact['contact_sp_title']))) { // Contact has a spouse. // Prep title $contact_title = $spouse_title = NULL; ! if ( (empty($contact['contact_title']) && empty($contact['contact_sp_title'])) || in_array('no_title', $options) ) { // No titles. Go without. } elseif (empty($contact['contact_sp_title'])) { // Title for contact but not spouse. Only can handle if Mr. if ($contact['contact_title'] == 'Mr' || $contact['contact_title'] == 'Mr.') { $contact['contact_sp_title'] = str_replace('Mr', 'Mrs', $contact['contact_title']) $contact_title = $contact['contact_title']; Extract refactor if ($this->hasSpouse()) { if ( ! $this->hasTitles()) { $this->duplicateSpouseTitleIfNeeded(); $this->appendMiddleNames();
  26. <?php ! class ContactName { protected $contact_array; protected $contact; protected

    $options; ! public function __construct(array $contact_array, array $options = array()) { $this->contact_array = $contact_array; $this->options = $options; $this->processContactArray(); } ! public function output() { return $this->getFullName(); } ! protected function getFullName() { if ($this->hasSpouse()) { return $this->getFullNameWithSpouse(); } else { return $this->getFullNameIndividual(); } Extract refactor result <?php ! include_once('ContactName.php'); ! $name = new ContactName( $array_of_name_stuff_from_sql ); echo $name->output(); Example use:
  27. Not set in stone 23 patterns in “Design Patterns” 13

    patterns in “Code Complete” 49 patterns on WIkipedia
  28. <?php $pos = strpos($tnt_ini,'RedirectQueryIni'); if($pos !== false){ // Find new

    query ini function get_between($input, $start, $end) { return substr($input, strlen($start)+strpos($input, $start), (strlen($input) - strpos($input, $end))*(-1)); } $new_ini = trim(get_between($tnt_ini, 'RedirectQueryIni', "\n")); if(empty($new_ini)) { $new_ini = trim(substr($tnt_ini, $pos+strlen('RedirectQueryIni')+1)); $pos = strpos($new_ini, "\n"); if($pos !== false) { $new_ini = $url; } } ! // Compare with existing if($new_ini!=$url) { // Update tnt_orgs with new ini $this->db ->set('tnt_org_ini', $new_ini)
  29. def. Standards agreements within any group to adhere to a

    convention. Benefits: consistency, interoperability, comprehension
  30. read Gang of Four Head First Design Patterns PHP Objects,

    Patterns, and Practice Brandon Savage’s “PHP Design Patterns”
  31. <?php namespace Karani\Contacts; ! // Located in /lib/Karani/Contacts/Name.php ! class

    Name { protected $contact_array; protected $contact; protected $options; ! public function __construct(array $contact_array, array $options = array()) { $this->contact_array = $contact_array; $this->options = $options; $this->processContactArray(); } ! public function output() { return $this->getFullName(); } ! protected function getFullName() { if ($this->hasSpouse()) { return $this->getFullNameWithSpouse(); PSR-4 Name Class <?php ! $name = new Karani\Contacts\Name( $array_of_name_stuff_from_sql ); echo $name->output(); Example use:
  32. { "name": "mattstauffer/peersconf", "description": "PeersConf Demo", "require": { "nesbot/carbon": "1.6.*",

    "php": ">=5.3.0", "laravel/framework": "4.0.*", "dflydev/markdown": "1.0.*", "iron-io/iron_mq": "1.4.*", "filp/whoops": "1.0.*", "bugsnag/bugsnag": "2.*", "mockery/mockery": "0.*", "psr/log": "1.0.*", "imagine/imagine": "v0.5.0", "iron-io/iron_worker": "1.4.1" }, "autoload": { "psr-0": { "PeersConf": "application/", }, }, "config": { }, "require-dev": { "fzaninotto/Faker": "dev-master", "phpunit/phpunit": "3.7.*" composer.json
  33. In my CMS... Plugins Dukt & Exp:resso for EE examples

    Craft: Adrian Macneil “Cocktail Recipes” TD’s Craft Guzzle for wrapping packages
  34. The Matt Stauffer Authorized Definition Testing: Making sure a piece

    of code does what it's supposed to, so you can find out if it’s broken*now*, rather than when an angry customer emails you.
  35. <?php ! class nameTest extends CIUnit_TestCase { public function testHandlesSimpleIndividual()

    { $output_name = 'John Doe'; ! $name = new Name($this->simple_john_doe, array()); ! $this->assertEquals($output_name, $name->output()); } ! public function testHandlesSimpleIndividualReversed() { $output_name = 'Doe, John'; ! $name = new Name($this->simple_john_doe, array('reversed')); ! $this->assertEquals($output_name, $name->output()); } ! public function testHandlesDecoratedIndividual() { $output_name = 'Dr. John Edward Doe MD'; ! $name = new Name($this->decorated_john_doe, array());
  36. Recap 1. Embrace the object 2. Learn from those who

    came before you 3. Play nice with others
  37. Recap 1. Embrace the object 2. Learn from those who

    came before you 3. Play nice with others 4. Be lazy
  38. Recap 1. Embrace the object 2. Learn from those who

    came before you 3. Play nice with others 4. Be lazy 5. Cover your butt
  39. One Final Note This is not what you “should do”

    because it’s nice. ! This is what you “should do” because if you do it your life will be better and you will make more money.
  40. Writing modular, reusable code. Not reinventing the wheel. No lone

    cowboys. Learning from our peers & predecessors. Finding the bugs before the clients do. Having confidence to make changes in the future. Reducing code rot & technical debt. Making life easier. Thanks. @stauffermatt