The State of PHP: 2013

The State of PHP: 2013

2c91f8dbf323072a564dbc4d97a0b627?s=128

Aaron Kuzemchak

June 18, 2013
Tweet

Transcript

  1. The State of PHP Aaron Kuzemchak June 2013 • Richmond,

    VA kuzemchak.net • @akuzemchak • github.com/akuzemchak
  2. About me

  3. Senior Application Developer Visual Chefs www.visualchefs.com

  4. •Husband & father •Guitarist •Gamer •90’s rock enthusiast •Standard nerd

  5. Where to !nd me Blog kuzemchak.net Twitter @akuzemchak GitHub akuzemchak

  6. None
  7. Three little letters So much drama

  8. None
  9. None
  10. None
  11. None
  12. None
  13. None
  14. When critics think of PHP, they think of version 4

  15. Yeah, PHP 4 sucked

  16. Pros didn’t have the tools they needed

  17. Amateurs didn’t have to be good to make something

  18. Popular projects had crappy code and encouraged bad practices *ahem*

    *cough*
  19. Time changes everything

  20. PHP 5 was good 5.3 was really good

  21. None
  22. None
  23. None
  24. None
  25. None
  26. Common criticisms (and rebuttals)

  27. PHP: A Fractal of Bad Design bit.ly/IDW2RA Biased hate speech

  28. “Naming is inconsistent”

  29. This is true, to some degree However...

  30. Classes are always in StudlyCaps

  31. Methods are always in camelCase

  32. Functions are always in snake_case

  33. Constants are always in ALL_CAPS

  34. “Needle/haystack order issue is ridiculous”

  35. Yeah, it’s kind of annoying, but...

  36. Array functions are always needle => haystack

  37. String functions are always haystack => needle

  38. “That’s stupid... how am I supposed to remember that?”

  39. Read the docs... they’re pretty good www.php.net/manual/en/

  40. “PHP is weak typed”

  41. So always use === and type juggling bit.ly/6j998Q

  42. “Error handling is terrible”

  43. So convert errors to Exceptions bit.ly/rxRqWR

  44. Enough of that

  45. Goals of this talk

  46. For those who dislike PHP: Show you how it’s improved

  47. For those who use PHP: Make sure you’re doing it

    right
  48. For non-coders: Eh, well, you may want to go grab

    a co!ee or something
  49. Most of this talk is inspired by PHP: The Right

    Way www.phptherightway.com
  50. Let’s get rolling

  51. Language improvements

  52. Syntax improvements PHP 5.4+

  53. // Works on all versions $my_array = array('foo', 'bar', 'baz');

    // Works on 5.4+ $my_array = ['foo', 'bar', 'baz']; Array syntax
  54. Instant access to new objects // Old way $foo =

    new Foo(); $bar = $foo->bar(); // New better way $bar = (new Foo())->bar();
  55. Instant access to returned arrays // Old way $foo =

    new Foo(); $bar = $foo->bar(); $first = $bar[0]; // New better way $first = (new Foo())->bar()[0];
  56. Namespaces

  57. Prevent naming con"icts

  58. Keep things organized

  59. // modules/blog/Post.php class Post { /**/ } // modules/news/Post.php class

    Post { /**/ } $post = new Post();
  60. Fatal error: Cannot redeclare class Post

  61. // modules/blog/Post.php namespace Blog; class Post { /**/ } //

    modules/news/Post.php namespace News; class Post { /**/ } $post = new Blog\Post(); 
  62. namespace Blog; use Auth\AuthorInterface; use News\Post as NewsPost; class Post

    extends NewsPost { protected $author; protected $tags = []; protected $publish_date; public function __construct(AuthorInterface $author, array $tags) { $this->author = $author; $this->tags = $tags; } public function setPublishDate(\DateTime $date) { $this->publish_date = $date; } }  
  63. Closures

  64. AKA: “anonymous functions”

  65. Very common in languages like JavaScript Now extremely popular in

    PHP
  66. $is_even = function ($number) { return $number % 2 ===

    0; }; $is_even(1);
  67. Many core PHP functions accept closures

  68. $numbers = [1, 2, 3, 4, 5]; $type = 'even';

    $evens = array_filter($numbers, function ($number) use ($type) { $remainder = ($type === 'even') ? 0 : 1; return $number % 2 === $remainder; }); 
  69. Object-oriented, fo’ realz

  70. •Visibility •Type hinting •Inheritance •Abstract classes •Cloning •Magic methods •Interfaces

    •Traits
  71. Type hinting

  72. Force methods to only accept certain types of arguments

  73. class Post { protected $author; protected $tags = []; public

    function __construct(Author $author, array $tags) { $this->author = $author; $this->tags = $tags; } }  
  74. Magic methods

  75. Allow us to hook into class behavior

  76. class Foo { protected $attributes = []; public function getFoo()

    { return $this->attributes['foo']; } public function setFoo($value) { $this->attributes['foo'] = $value; } public function getBar() { return $this->attributes['bar']; } public function setBar($value) { $this->attributes['bar'] = $value; } } $foo = new Foo(); $foo->setBar('bar'); $bar = $foo->getBar();
  77. class Foo { protected $attributes = []; public function __get($key)

    { return $this->attributes[$key]; } public function __set($key, $value) { $this->attributes[$key] = $value; } } $foo = new Foo(); $foo->bar = 'bar'; $bar = $foo->bar;  
  78. class MyClass { protected $sub_class; public function __construct(SubClass $sub_class) {

    $this->sub_class = $sub_class; } public function __call($method, $args) { return call_user_func_array([$this->sub_class, $method], $args); } } $my_class = new MyClass(new SubClass()); $my_class->someSubClassMethod(); 
  79. class Post { protected $title; protected $author; public function __construct($title,

    Author $author) { $this->title = $title; $this->author = $author; } public function __toString() { return $this->title . ', by ' . $this->author->getName(); } } $post = new Post('My First Post', new Author('Me')); echo $post; 
  80. Interfaces

  81. Blueprints for classes

  82. interface AuthorInterface { public function getName(); public function getRecentPosts($limit =

    10); } class GuestAuthor implements AuthorInterface { protected $id; protected $name; public function getName() { return $this->name . ' (guest author)'; } public function getRecentPosts($limit = 10) { return Post::where('author_id', '=', $this->id) ->limit($limit) ->get(); } } 
  83. Useful in combination with type hinting

  84. class FeaturedAuthor implements AuthorInterface { /**/ } class GuestAuthor implements

    AuthorInterface { /**/ } class Post { protected $author; protected $tags = []; public function __construct(AuthorInterface $author, array $tags) { $this->author = $author; $this->tags = $tags; } public function getAuthorName() { return $this->author->getName(); } } 
  85. Traits PHP 5.4+

  86. An alternative to multi-class inheritance

  87. Think of as “mix-ins” for classes

  88. trait Taggable { public function getTags() { return Tag::where('post_id', '=',

    $this->id)->get(); } public function countTags() { return Tag::where('post_id', '=', $this->id)->count(); } }
  89. trait Categorizeable { public function getCategories() { return Category::where('post_id', '=',

    $this->id)->get(); } public function countCategories() { return Category::where('post_id', '=', $this->id)->count(); } }
  90. class Post { use Taggable, Categorizeable; protected $id; public function

    __construct($id = null) { $this->id = $id; } } $post = new Post(1); $tags = $post->getTags(); $categories = $post->getCategories(); 
  91. Exceptions

  92. Try... fail... catch... handle

  93. try { throw new Exception('Something went wrong!'); } catch (Exception

    $e) { echo $e; // Message and stack trace echo $e->getMessage(); // Message only var_dump($e->getTrace()); // Stack trace as array }
  94. try { // Connect to database // Build post object

    // Save to database } catch (PDOException $e) { die('Could not connect to database.'); } catch (Exception $e) { die($e->getMessage()); }
  95. PHP !nally has “!nally” PHP 5.5+

  96. try { // Connect to database // Build post object

    // Save to database } catch (PDOException $e) { echo 'Could not connect to database.'; } catch (Exception $e) { echo $e->getMessage(); } finally { echo 'Done!'; }
  97. Convert normal errors to exceptions

  98. set_error_handler(function ($errno, $errstr, $errfile, $errline) { throw new ErrorException($errstr, $errno,

    0, $errfile, $errline); });
  99. Fallback exception handler

  100. set_exception_handler(function (Exception $e) { // Show a fancy page with

    message and stack trace });
  101. Date and time with... DateTime

  102. // Set the default timezone date_default_timezone_set('UTC'); // Current date/time in

    default timezone $utc_now = new DateTime(); echo $utc_now->format(DateTime::RFC850); // Saturday, 01-Jun-13 23:51:25 UTC
  103. // Current date/time in a specific timezone $la_now = new

    DateTime('now', new DateTimeZone('America/Los_Angeles')); echo $la_now->format(DateTime::RFC850); // Saturday, 01-Jun-13 16:52:28 PDT // Change a timezone $la_now->setTimezone(new DateTimeZone('America/New_York')); echo $la_now->format(DateTime::RFC850); // Saturday, 01-Jun-13 19:52:28 EDT // Change the date/time $la_now->modify('+3 days'); echo $la_now->format(DateTime::RFC850); // Tuesday, 04-Jun-13 19:52:28 EDT
  104. Di#’ing with DateTime

  105. $now = new DateTime('today'); $later = new DateTime('today + 400

    days, 36 hours, 32 minutes'); $diff = $now->diff($later); echo $diff->format('%y years, %m months, %d days, %h hours, %i minutes, %s seconds'); // 1 years, 1 months, 5 days, 12 hours, 32 minutes, 0 seconds echo $diff->format('%a total days'); // 401 total days
  106. PDO PHP Data Objects

  107. One interface for all database types

  108. // Let's use MySQL $db = new PDO('mysql:host=localhost;dbname=test', 'root', 'root');

    // No wait, let's use SQLite $db = new PDO('sqlite:/db/test.sqlite'); $posts = $db->query('select * from posts limit 10'); foreach ($posts->fetchAll() as $post) { // Do something }
  109. Security and speed with prepared statements More code, but worth

    it
  110. $posts = $db->prepare('select * from posts where author_id = :author_id

    and status = :status'); $posts->bindValue(':author_id', $_GET['author_id'], PDO::PARAM_INT); $posts->bindValue(':status', 'published', PDO::PARAM_STR); $posts->execute(); foreach ($posts->fetchAll() as $post) { // Do something }
  111. Don’t worry, there are some awesome ORMs and query builders

    that make PDO more fun
  112. •Illuminate Database •Doctrine DBAL •NotORM •Idiorm & Paris

  113. $posts = DB::table('posts') ->where('author_id', '=', $_GET['author_id']) ->where('status', '=', 'published') ->get();

    foreach ($posts as $post) { // Do something }
  114. Generators PHP 5.5+

  115. Build simple, memory- e$cient iterators

  116. // Cube each number in an array function cube(array $numbers)

    { foreach ($numbers as $number) { yield $number * $number * $number; } } // Display results foreach (cube([2, 4, 6]) as $number) { echo $number; } 
  117. Password hashing API PHP 5.5+

  118. // Create the hash $encrypted = password_hash('changeme!', PASSWORD_DEFAULT); // Check

    a value against the hash if (password_verify('changeme!', $encrypted) === true) { echo 'Good to go!'; } else { echo 'Ha! Wrong!'; }
  119. Backported to PHP 5.3 and 5.4! github.com/ircmaxell/password_compat

  120. Methodology changes

  121. PSR standards PHP Speci"cation Request

  122. Drafted and voted on by PHP-FIG Framework Interop Group www.php-"g.org

  123. These are not requirements for writing PHP

  124. They are guidelines for framework authors But many devs are

    adopting these
  125. PSR-0 Autoloading standard www.php-"g.org/psr/0/

  126. Map namespaces to !les

  127. // Autoloaded from /path/to/lib/Module/News/Post.php $post = new Module\News\Post(); // Same

    here $post = new Module_News_Post();
  128. PSR-1 Basic coding standard www.php-"g.org/psr/1/

  129. •Only <?php and <?= tags allowed •Files must be UTF-8

    •Files should declare symbols or cause “side-e!ects” •Symbols should follow core naming conventions
  130. PSR-2 Coding style guide www.php-"g.org/psr/2/

  131. •Files must use Unix LF •Soft limit of 120 characters

    •Opening braces on same line, except for classes & methods •Visibility must be declared for all properties & methods •4 spaces for indenting... no tabs
  132. Spaces Tabs VS.

  133. Spaces wins

  134. Composer getcomposer.org

  135. A better dependency/ package manager

  136. PHP’s answer to RubyGems, PIP, NPM, etc.

  137. What about PEAR?

  138. “No one on the planet has ever enjoyed PEAR.” Je!rey

    Way
  139. Dependencies handled on a per-project basis

  140. # Install Composer curl -sS https://getcomposer.org/installer | php # Optional:

    move to central location mv composer.phar /usr/local/bin/composer
  141. // composer.json { "require": { "monolog/monolog": "1.6.*", "swiftmailer/swiftmailer": "5.1.*", "illuminate/database":

    "4.0.*" }, "minimum-stability": "dev" }
  142. # Install dependencies (first time) php composer.phar install # Update

    dependencies (later on when needed) php composer.phar update
  143. // Enables autoloading, including PSR-0 require 'vendor/autoload.php'; $logger = new

    Monolog\Logger(); $message = Swift_Message::newInstance(); $db = new Illuminate\Database\Capsule\Manager();
  144. // composer.json { "require": { "monolog/monolog": "1.6.*", "swiftmailer/swiftmailer": "5.1.*", "illuminate/database":

    "4.0.*" }, "autoload": { "psr-0": { "Module": "path/to/modules/" }, "classmap": ["lib/"], "files": ["bootstrap/start.php"] } "minimum-stability": "dev" }
  145. As frameworks “package” their code, everyone else bene!ts

  146. Find packages at Packagist packagist.org

  147. None
  148. Packagist is my !rst stop when looking for PHP libraries

  149. If you’ve authored a library, post it there!

  150. Dependency injection

  151. $20 phrase for a $2 concept

  152. “Dependency injection is a software design pattern that allows removing

    hard-coded dependencies and making it possible to change them, whether at run-time or compile- time.” Wikipedia
  153. By passing dependencies, we can swap them out easily

  154. class Post { protected $db; public function __construct() { $this->db

    = new PDO('mysql:host=localhost;dbname=test', 'root', 'root'); } public function find($id) { $posts = $this->db->prepare('select * from posts where id = ? limit 1'); $posts->bindValue(1, $id, PDO::PARAM_INT); $posts->execute(); return $posts; } } // Find a post $post = new Post(); $first = $post->find(1); 
  155. class Post { protected $db; public function __construct(PDO $db) {

    $this->db = $db; } public function find($id) { $posts = $this->db->prepare('select * from posts where id = ? limit 1'); $posts->bindValue(1, $id, PDO::PARAM_INT); $posts->execute(); return $posts; } } $db = new PDO('mysql:host=localhost;dbname=test', 'root', 'root'); // Find a post $post = new Post($db); $first = $post->find(1);  
  156. Containers help manage things

  157. Pimple pimple.sensiolabs.org

  158. Illuminate Container github.com/illuminate/container

  159. $container = new Pimple(); $container['db'] = $container->share(function ($c) { return

    new PDO('mysql:host=localhost;dbname=test', 'root', 'root'); }); 
  160. class Post { protected $container; protected $db; public function __construct(Pimple

    $container) { $this->container = $container; $this->db = $container['db']; } public function find($id) { $posts = $this->db->prepare('select * from posts where id = ? limit 1'); $posts->bindValue(1, $id, PDO::PARAM_INT); $posts->execute(); return $posts; } } // Find a post $post = new Post($container); $first = $post->find(1);  
  161. $container = new Pimple(); $container['environment'] = 'local'; $container['db.local'] = array(

    'dsn' => 'mysql:host=localhost;dbname=test', 'user' => 'root', 'password' => 'root', ); $container['db.production'] = array( 'dsn' => 'mysql:host=localhost;dbname=production', 'user' => 'dbadmin', 'password' => 'P@55w0rd', ); $container['db'] = $container->share(function ($c) { $env = $c['environment']; $config = $c["db.{$env}"]; return new PDO($config['dsn'], $config['user'], $config['password']); });
  162. Testing

  163. PHP devs are embracing the testing culture

  164. PHPUnit is the standard for unit testing phpunit.de/manual/current/en/index.html

  165. Behat allows you to test business logic (BDD) behat.org

  166. Mink works in tandem with Behat for acceptance testing mink.behat.org

  167. phpspec for speci!cation- driven development www.phpspec.net

  168. Codeception brings them all together w/ helpers and accessible syntax

  169. $I = new WebGuy($scenario); $I->wantTo('Create wiki page'); $I->amOnPage('/'); $I->click('Pages'); $I->click('New');

    $I->see('New page'); $I->fillField('title', 'The Rock'); $I->fillField('body', 'Starring Nicolas Cage'); $I->click('Save'); $I->see('Page created'); $I->see('The Rock', 'h1'); $I->seeInCurrentUrl('pages/the-rock'); $I->seeInDatabase('pages', ['title' => 'The Rock']);
  170. Frameworks

  171. Lots of innovative new frameworks have emerged in the last

    two years
  172. Traditional MVC is old- school

  173. CodeIgniter is on life support Please Ellislab, just pull the

    plug... it’s su!ered enough already
  174. Micro is the new big thing Start with the basics,

    add to as needed Composer to the rescue!
  175. Slim slimframework.com

  176. •Powerful routing engine •Flexible view system •Basic session functionality •Middleware

    •Extremely well documented •Easy to get up and running
  177. $app = new Slim\Slim(); $app->get('/', function () { echo 'Home';

    }); $app->get('/posts', function () { echo 'All posts'; }); $app->get('/posts/:id', function ($id) { echo 'Post with ID of ' . $id; })->conditions(['id' => '\d+']); $app->post('/posts', function () { echo 'Create new post'; }); $app->run();
  178. Silex silex.sensiolabs.org

  179. Similar to Slim, but goes much deeper

  180. Built on Symfony components By Symfony developers

  181. •Pimple is the backbone •Uses HttpFoundation under the hood •Lots

    of built-in service providers •Well documented
  182. $app = new Silex\Application(); $app->register(new Silex\Provider\ServiceControllerServiceProvider()); $app['controllers.post'] = $app->share(function ($app)

    { return new PostController(); }); $app->get('/', function () { return 'Home'; }); $app->get('/posts', 'controllers.post:index'); $app->get('/posts/{id}', 'controllers.post:show')->assert('id', '\d+'); $app->post('/posts', 'controllers.post:create'); $app->run();
  183. class PostController { public function index() { return 'Home'; }

    public function show($id) { return 'Post with ID of ' . $id; } public function create() { return 'Create new post'; } }
  184. Laravel laravel.com

  185. Taking the PHP world by storm

  186. Built by a .NET guy from Arkansas

  187. •Complete, full-stack framework •Built on Symfony components •Beautiful, expressive syntax

    •Best ORM around •Powerful CLI •Magical DI container •Thorough documentation •Awesome community
  188. Route::get('/', function () { return 'Home'; }); Route::get('posts', function ()

    { return 'All posts'; }); Route::get('posts/{id}', function ($id) { return 'Post with ID of ' . $id; })->where('id', '\d+'); Route::post('posts', function () { return 'Create new post'; });
  189. Route::get('posts', function () { $posts = Post::all(); return View::make('posts.index', [

    'posts' => $posts, ]); }); Route::get('posts/{id}', function ($id) { $post = Post::find($id); return View::make('posts.show', [ 'post' => $post, ]); })->where('id', '\d+');
  190. @extends('layouts.main') @section('content') <h1>{{ $post->title }}</h1> {{ $post->content }} <h2>Comments</h2> @foreach($post->comments

    as $comment) <div class="comment"> {{ $comment->content }}<br> <em>{{ $comment->author }}</em> </div> @endforeach @stop
  191. Route::post('posts', function () { $val = Validator::make(Input::get(), [ 'title' =>

    'required|unique:posts', 'content' => 'required', 'status' => 'required|in:open,closed', ]); if ($val->fails()) { return Redirect::back()->withErrors($val)->withInput(); } $post = new Post(Input::get()); $post->save(); return Redirect::to('posts/' . $post->id); });
  192. Final thoughts

  193. PHP has grown up quite a bit

  194. I hope that I have demonstrated this for you

  195. Go read PHP: The Right Way www.phptherightway.com

  196. Embrace Composer

  197. Try the newer frameworks And help make them even better!

  198. Otherwise, don’t be a hater

  199. Aaron Kuzemchak Visual Chefs kuzemchak.net • @akuzemchak • github.com/akuzemchak Thanks

    for listening!