Refactoring PHP/Symfony2 apps

51
REFACTORING PHP/SYMFONY2 APPS

description

Slides for deSymfony 2013

Transcript of Refactoring PHP/Symfony2 apps

Page 1: Refactoring PHP/Symfony2 apps

REFACTORING PHP/SYMFONY2 APPS

Page 2: Refactoring PHP/Symfony2 apps

Thank you to all our sponsors!

Page 3: Refactoring PHP/Symfony2 apps

Raúl Fraile

• Software developer at

• PHP 5.3 Zend Certified Engineer

• Symfony Certified Developer

• LadybugPHP

raulfraile

Page 4: Refactoring PHP/Symfony2 apps

1. Refactoring 1012. Coding Standard3. IDE4. Code/Data separation5. Environment coupling6. Don’t Repeat Yourself7. Fat controllers

Agenda

Page 5: Refactoring PHP/Symfony2 apps

Test project

Page 6: Refactoring PHP/Symfony2 apps

APIJokes (I)GET /api/list Get the jokes list in JSONPOST /api/add Add a new jokePOST /api/edit Edit an existing jokeGET / Public website with the joke list in HTML

Page 7: Refactoring PHP/Symfony2 apps

APIJokes (II)

Special cases

Every time a joke is added or edited, an email is sent to the administrator.

We don’t allow jokes about Java.

Page 9: Refactoring PHP/Symfony2 apps

Refactoring 101

Page 10: Refactoring PHP/Symfony2 apps

Rewrite VS Refactor

Page 11: Refactoring PHP/Symfony2 apps

Rewrite

http://www.flickr.com/photos/meliah/2601885140/

Page 12: Refactoring PHP/Symfony2 apps

http://www.flickr.com/photos/90692443@N05/8239219385/

Refactor

Page 13: Refactoring PHP/Symfony2 apps

Refactoring is a continuous process

Page 14: Refactoring PHP/Symfony2 apps
Page 15: Refactoring PHP/Symfony2 apps

Always taking a software that works...

Page 16: Refactoring PHP/Symfony2 apps

...and which has tests

Page 17: Refactoring PHP/Symfony2 apps

Coding Standard

Page 18: Refactoring PHP/Symfony2 apps

http://www.flickr.com/photos/zpeckler/2835570492/

Change my CS?!

Page 19: Refactoring PHP/Symfony2 apps

Important: Choose a CS and be consistent

Page 20: Refactoring PHP/Symfony2 apps

Open source projects: share the same CS

Page 21: Refactoring PHP/Symfony2 apps

PSR-1/2

Page 22: Refactoring PHP/Symfony2 apps
Page 23: Refactoring PHP/Symfony2 apps

http://cs.sensiolabs.org

Page 24: Refactoring PHP/Symfony2 apps

php-cs-fixer fix ApiJokesBundle/ --level=all --dry-run --diff -v

1) Controller/WebsiteController.php (braces, return) ---------- begin diff ---------- --- Original +++ New @@ @@ -class WebsiteController extends Controller { - public function indexAction() { +class WebsiteController extends Controller +{ + public function indexAction() + { $em = $this->getDoctrine()->getManager(); $jokes = $em->getRepository('...')->findAll(); + return $this->render('...', array( 'jokes' => $jokes )); } } ---------- end diff ----------

Page 25: Refactoring PHP/Symfony2 apps

IDE

Page 26: Refactoring PHP/Symfony2 apps

Problems: DIC, foreach, repositories...

Page 27: Refactoring PHP/Symfony2 apps

<?php $mailer = $this->get('mailer');$mailer->send($message);

Page 28: Refactoring PHP/Symfony2 apps

<?php /** @var $mailer \Swift_Mailer */$mailer = $this->get('mailer');

$mailer->send($message);

Page 29: Refactoring PHP/Symfony2 apps

Type hints, not only to help the IDE...

Page 30: Refactoring PHP/Symfony2 apps

<?php $data = array_map(function ($item) { return array( 'id' => $item->getId(), 'content' => $item->getContent() );}, $jokes);

Page 31: Refactoring PHP/Symfony2 apps

<?php $data = array_map(function (Joke $item) { return array( 'id' => $item->getId(), 'content' => $item->getContent() );}, $jokes);

Page 32: Refactoring PHP/Symfony2 apps

Code/Data separation

Page 33: Refactoring PHP/Symfony2 apps

They are differenthttp://www.flickr.com/photos/yukariryu/121153772/

Page 34: Refactoring PHP/Symfony2 apps

A change in the data or configuration should’t

require changing the code

Page 35: Refactoring PHP/Symfony2 apps

public function load(ObjectManager $manager){ $jokes = array( 'There’s no place like 127.0.0.1', 'If at first you don’t succeed; call it version 1.0', 'Beware of programmers that carry screwdrivers', 'What color do you want that database?' );  foreach ($jokes as $item) { $joke = new Joke(); $joke->setContent($item);  $manager->persist($joke); }  $manager->flush();}

Page 36: Refactoring PHP/Symfony2 apps

# fixtures/joke.yml

jokes: - 'I would love to change the world, but they won’t...' - 'There’s no place like 127.0.0.1' - 'If at first you don’t succeed; call it version 1.0' - 'You know it’s love when you memorize her IP...' - 'Beware of programmers that carry screwdrivers' - 'Best file compression around: “rm *.*” = 100...' - 'The truth is out there…anybody got the URL?' - 'What color do you want that database?'

Page 37: Refactoring PHP/Symfony2 apps

public function load(ObjectManager $manager){ $jokes = Yaml::parse(__DIR__ . '/fixtures/joke.yml');  foreach ($jokes['jokes'] as $item) { $joke = new Joke(); $joke->setContent($item);  $manager->persist($joke); }  $manager->flush();}

Page 38: Refactoring PHP/Symfony2 apps

Environment coupling

Page 39: Refactoring PHP/Symfony2 apps

http://www.flickr.com/photos/darkhornet/4945282009/

FAIL

Page 40: Refactoring PHP/Symfony2 apps

{% if app.environment == 'prod' %} <script type="text/javascript"> // Google Analytics code </script>{% endif %}

Page 41: Refactoring PHP/Symfony2 apps

The software shouldn’t take any decision based

on the environment

Page 42: Refactoring PHP/Symfony2 apps

Better approach: different configuration for each environment

Page 43: Refactoring PHP/Symfony2 apps

{% if enable_analytics %} <script type="text/javascript"> // Google Analytics code </script>{% endif %}

Page 44: Refactoring PHP/Symfony2 apps

Don’t Repeat Yourself

Page 45: Refactoring PHP/Symfony2 apps

// do not allow jokes about java)if (stripos($content, 'java') !== false) { throw new BadRequestHttpException( 'Java jokes are not allowed' );}

Page 46: Refactoring PHP/Symfony2 apps

use Symfony\Component\Validator\Constraint; /** * @Annotation */class ContainsJava extends Constraint{ public $message = 'Java jokes are not allowed';}

Page 47: Refactoring PHP/Symfony2 apps

 use Symfony\Component\Validator\Constraint;use Symfony\Component\Validator\ConstraintValidator; class ContainsJavaValidator extends ConstraintValidator{ public function validate($value, Constraint $constraint) { if (stripos($value, 'java') !== false) { $this->context->addViolation($constraint->message); } }}

Page 48: Refactoring PHP/Symfony2 apps

Fat controllers

Page 49: Refactoring PHP/Symfony2 apps

Go on a diet!

http://www.flickr.com/photos/golf_pictures/3017331832/

Page 50: Refactoring PHP/Symfony2 apps

Configuration, events, annotations, services, simpler methods, inheritance, param

converters...