The Wonderful World of Symfony Components
Upcoming SlideShare
Loading in...5
×
 

The Wonderful World of Symfony Components

on

  • 13,827 views

Wow, Symfony Components! ...

Wow, Symfony Components!

In this talk, we'll look at the history of PHP, and the struggles as a community to create shared libraries between our large community. Find out the significance of PSR-0 and Composer in *your* life and how you can leverage libraries from all of PHP in your projects.

We'll also look at the most fundamental Symfony2 components - HttpFoundation, HttpKernel, EventDispatcher, & Routing - including those that have been adopted by Drupal 8. We'll also check out a bunch of the other interesting Symfony2 components that can be used as tools in any PHP project.

The goal of this talk is to show you just how easy finding and using high quality libraries has become in PHP. By the end, you'll be excited and ready to high-five all of your PHP friends.

Statistics

Views

Total Views
13,827
Views on SlideShare
12,648
Embed Views
1,179

Actions

Likes
25
Downloads
85
Comments
1

18 Embeds 1,179

http://www.symfony.es 367
http://symfony.es 222
http://planet.ezocast.net 167
http://iakio.hatenablog.com 160
https://twitter.com 135
http://d.hatena.ne.jp 101
http://www.php-talks.com 7
http://www.techgig.com 3
http://www.craftitonline.com 3
http://webcache.googleusercontent.com 2
http://twitter.com 2
http://localhost 2
https://twimg0-a.akamaihd.net 2
https://si0.twimg.com 2
http://translate.googleusercontent.com 1
http://www.twylah.com 1
http://www.linkedin.com 1
http://symfony2developer.com 1
More...

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

The Wonderful World of Symfony Components The Wonderful World of Symfony Components Presentation Transcript

  • The Wonderful World of the Symfony Components by your friend: Ryan Weaver @weaverryanFriday, September 28, 12
  • Who is this dude? • That “Docs” guy • KnpLabs US - Symfony consulting, training, Kumbaya • Writer for KnpUniversity.com screencasts • Husband of the much more talented @leannapelham knplabs.com @weaverryan github.com/weaverryanFriday, September 28, 12
  • Intro Life before components (The Desert of the Real)Friday, September 28, 12 View slide
  • Symfony consists of 23 individual componentsFriday, September 28, 12 View slide
  • Components are available via ComposerFriday, September 28, 12
  • Snooze...Friday, September 28, 12
  • Who Cares?Friday, September 28, 12
  • 1 We Suck at Sharing and that sucks for you @weaverryanFriday, September 28, 12
  • Including External Libraries is DepressingFriday, September 28, 12
  • The Big Bummer :( • How do I autoload their files? • Does their library depend on anything else? • How do I even store their files in my project? @weaverryanFriday, September 28, 12
  • Include a Zend Framework 1 component in symfony1Friday, September 28, 12
  • Friday, September 28, 12
  • manually download the library and commit it into your projectFriday, September 28, 12
  • if you don’t want the WHOLE library, carefully delete everything except the dependent componentsFriday, September 28, 12
  • autoloading is completely custom ... blah gross!Friday, September 28, 12
  • 2 Components are the key to mastering your framework @weaverryanFriday, September 28, 12
  • 3 If PHP is big, we’ll thrive If PHP is small, we’ll die @weaverryanFriday, September 28, 12
  • Communities http://www.flickr.com/photos/kitty-kat/ PHP is Huge! Right? @weaverryanFriday, September 28, 12
  • PHP > Ruby @weaverryanFriday, September 28, 12
  • Fragmentation http://www.flickr.com/photos/slpunk99/7329609744 But fragmentation makes us tiny, isolated, and misguided trend-setters @weaverryanFriday, September 28, 12
  • PHP frameworks < Rails @weaverryanFriday, September 28, 12
  • I don’t want a damned CakePHP Plugin! CakePHP CodeIgniter @weaverryanFriday, September 28, 12
  • Fragmentation • More information we have to know • Difficult to hire • Disjointed forums, StackOverflow • Interoperability? What’s that? @weaverryanFriday, September 28, 12
  • Components are shareable across all of PHPFriday, September 28, 12
  • Act 1 Making Sharing SexyFriday, September 28, 12
  • PHP Framework Interoperability Group http://www.php-fig.org/Friday, September 28, 12
  • The United Nations of PHPFriday, September 28, 12
  • (A) The Problem of AutoloadingFriday, September 28, 12
  • My Autoloader doesn’t like your PHPs • The symfony1 autoloader doesn’t know where ZF1 classes live. The Zf1 autoloader doesn’t know where symfony1 classes live • Each library has its own autoloader that you must discover, configure and use @weaverryanFriday, September 28, 12
  • like a town where every store has its own currencyFriday, September 28, 12
  • From The Mountain: PSR-0 Class Naming Conventions “Thou shalt name your classes by following a predictable pattern” @weaverryanFriday, September 28, 12
  • class: sfRequest path: lib/vendor/symfony/???idkFriday, September 28, 12
  • class: SymfonyComponentHttpFoundationRequest path: vendor/symfony/src/Symfony/Component/ HttpFoundation/Request.phpFriday, September 28, 12
  • use SymfonyComponentClassLoaderUniversalClassLoader; $loader = new UniversalClassLoader(); $loader->registerNamespaces(array( Zend => __DIR__./path/to/zf )); $loader->register();Friday, September 28, 12
  • (B) Managing Vendors in your projectFriday, September 28, 12
  • ComposerFriday, September 28, 12
  • 1) Composer.json { "require": { "zendframework/ldap": "2.0.x-dev" } }Friday, September 28, 12
  • 2) Composer.phar $ curl -s https://getcomposer.org/installer | php $ php composer.phar installFriday, September 28, 12
  • 3) Use It require vendor/autoload.php; use ZendLdapLdap; $options = array(...); $ldap = new Ldap($options);Friday, September 28, 12
  • Friday, September 28, 12
  • Act 2 Your friendly, neighborhood Symfony ComponentsFriday, September 28, 12
  • The Symfony Components • HttpFoundation • OptionsResolver • HttpKernel • Console • Event Dispatcher • Filesystem • Routing • Finder • CSSSelector • Locale • DomCrawler • Process • BrowserKit • Serializer • Config • Templating • Yaml • Form • DependencyInjection • Translation • Security • Validator @weaverryanFriday, September 28, 12
  • The Symfony Components • HttpFoundation • OptionsResolver • HttpKernel • Console • Event Dispatcher • Filesystem • Routing • Finder • CSSSelector • Locale • DomCrawler The Process • “Framework” • BrowserKit • Serializer Components • Config • Templating • Yaml • Form • DependencyInjection • Translation • Security • Validator @weaverryanFriday, September 28, 12
  • Level 1 HttpFoundation symfony/http-foundationFriday, September 28, 12
  • Fun to Install! { "require": { 1 "symfony/http-foundation": "2.1.x-dev" } } 2 php composer.phar install <?php // your app 3 require vendor/autoload.php; @weaverryanFriday, September 28, 12
  • ? ? Request ? Response /foo ? <h1>Hi!</h1> ? ? Mystery PHP Code @weaverryanFriday, September 28, 12
  • The RequestFriday, September 28, 12
  • GET /foo?p=v1 HTTP/1.1 Host: knplabs.com Accept: text/html User-Agent: Mozilla/5.0 // /foo?p=v1 $uri = $_SERVER[REQUEST_URI]; $p = isset($_GET[p]) ? $_GET[p] : ; $host = $_SERVER[HTTP_HOST]; $accept = $_SERVER[HTTP_ACCEPT]; $ua = $_SERVER[HTTP_USER_AGENT];Friday, September 28, 12
  • GET /foo?p=v1 HTTP/1.1 Host: knplabs.com Accept: text/html User-Agent: Mozilla/5.0 use SymfonyComponentHttpFoundationRequest; $request = Request::createFromGlobals(); $uri = $request->getPathInfo(); /foo $p = $request->query->get(p); $host = $request->getHost(); $accept = $request->headers->get(Accept);Friday, September 28, 12
  • The ResponseFriday, September 28, 12
  • HTTP/1.1 200 OK Date: Tue, 04 Jun 2011 21:05:05 GMT Content-Type: text/html Set-Cookie:foo=fooval; path=/; httponly <h1>Foo!</h1> header(Content-Type, text/html); setcookie(foo, fooval); echo <h1>Foo!</h1>;Friday, September 28, 12
  • HTTP/1.1 200 OK Date: Tue, 04 Jun 2011 21:05:05 GMT Content-Type: text/html Set-Cookie:foo=fooval; path=/; httponly <h1>Foo!</h1> use SymfonyComponentHttpFoundationResponse; use SymfonyComponentHttpFoundationCookie; $response = new Response(<h1>Foo!</h1>); $cookie = new Cookie(foo, fooval); $response->headers->setCookie($cookie); $response->send();Friday, September 28, 12
  • A Mini FrameworkFriday, September 28, 12
  • // not dependable! $uri = $_SERVER[REQUEST_URI]; ob_start(); if ($uri == /) { echo Homepage! o/; } else { echo sad missing page :/; } header(X-TOKEN: Foo); ob_end_flush();Friday, September 28, 12
  • Symfony3$req = Request::createFromGlobals();if ($req->getPathInfo() == /) { $res = new Response(Homepage! o/);} else { $res = new Response(sad missing page :/);}$res->headers->set(X-SECRET, Foo);$res->send();Friday, September 28, 12
  • Get some learning about HttpFoundation • Docs: http://bit.ly/sf2-http-foundation • Code: http://bit.ly/sf2-http-foundation-code • API: http://bit.ly/sf2-http-foundation-api @weaverryanFriday, September 28, 12
  • Level 2 HttpKernel symfony/http-kernelFriday, September 28, 12
  • Request Framework ? Determine ? the controller ? Execute it Response Flush the Response @weaverryanFriday, September 28, 12
  • Framework: A pattern for converting a request into a responseFriday, September 28, 12
  • Request Framework ? Determine ? the controller ? Execute it Response Flush the Response @weaverryanFriday, September 28, 12
  • Request HttpKernel ? Determine ? the controller ? Execute it Response Flush the Response @weaverryanFriday, September 28, 12
  • $kernel = new HttpKernel(...); $request = Request::createFromGlobals(); $response = $kernel->handle($request); $response->send(); There be magic inside!Friday, September 28, 12
  • These lines execute your Symfony application (hint, see - web/app.php)Friday, September 28, 12
  • ... and Drupal 8 too ...Friday, September 28, 12
  • Yea, this is abstract Stay Tuned...Friday, September 28, 12
  • Level 3 EventDispatcher symfony/event-dispatcherFriday, September 28, 12
  • A classic “observer pattern” Tell me when Liz gets to work Listener Event Dispatcher Listener Tell me when Liz gets to work @weaverryanFriday, September 28, 12
  • • Event Dispatcher: a classic “observer pattern” Yo! I’m at work! Listener Event Dispatcher Listener @weaverryanFriday, September 28, 12
  • • Event Dispatcher: a classic “observer pattern” Liz is at work! Listener Event Dispatcher Listener Liz is at work! @weaverryanFriday, September 28, 12
  • • Event Dispatcher: a classic “observer pattern” She’s the best around! Listener Event Dispatcher Listener I’m totally going to go talk with her!!Friday, September 28, 12
  • Level 4 Routing symfony/routingFriday, September 28, 12
  • Url Router Match against a route Route Return info about the route Route @weaverryanFriday, September 28, 12
  • $loader = new ClosureLoader(); $router = new Router($loader, function() { $collection = new RouteCollection(); $route = new Route(/blog/{slug}); $collection->add(blog, $route); return $collection; }); $results = $router->match(/blog/sflive); array( slug => sflive, _route => blog, )Friday, September 28, 12
  • $loader = new ClosureLoader(); $router = new Router($loader, function() { $collection = new RouteCollection(); $route = new Route(/blog/{slug}, array( _controller => SomeBundle:Blog:show )); $collection->add(blog, $route); return $collection; }); $results = $router->match(/blog/sflive); array( _controller => SomeBundle:Blog:show, slug => sflive, _route => blog )Friday, September 28, 12
  • Input: Request Information Output: An array of infoFriday, September 28, 12
  • Level 5 A “Framework”Friday, September 28, 12
  • $kernel = new HttpKernel(...); $request = Request::createFromGlobals(); $response = $kernel->handle($request); $response->send();Friday, September 28, 12
  • HttpKernel::handle() HttpFoundation + EventDispatcher + Routing (optional) = Framework @weaverryanFriday, September 28, 12
  • HttpKernel::handle() Request + Determine Controller + Controller Returns a Response = Response @weaverryanFriday, September 28, 12
  • Homework: Hack your Framework ... or do Karaoke later... your callFriday, September 28, 12
  • HttpKernel::handleRaw() private function handleRaw(Request $request, $type = self::MASTER_REQUEST) {     // request     $event = new GetResponseEvent($this, $request, $type);     $this->dispatcher->dispatch(KernelEvents::REQUEST, $event);     if ($event->hasResponse()) {         return $this->filterResponse($event->getResponse(), $request, $type);     }     // load controller The kewlest code I know about... seriously     if (false === $controller = $this->resolver->getController($request)) {         throw new NotFoundHttpException(Unable to find the controller);     }     $event = new FilterControllerEvent($this, $controller, $request, $type);     $this->dispatcher->dispatch(KernelEvents::CONTROLLER, $event);     $controller = $event->getController();     // ... returns a response } @weaverryanFriday, September 28, 12
  • step/var_dump()die; the core • SymfonyComponentHttpKernelHttpKernel::handle() http://bit.ly/sf2-http-kernel-code • Symfony Framework Important Classes SymfonyComponentHttpKernelEventListenerRouterListener - Executes the routing SymfomyBundleFrameworkBundleControllerControllerResolver - Parses _controller and instantiates the controller object @weaverryanFriday, September 28, 12
  • Timeline of events in the Symfony Framework kernel.request kernel.controller kernel.responseFriday, September 28, 12
  • Profile events in the Symfony FrameworkFriday, September 28, 12
  • The Symfony Components • HttpFoundation • OptionsResolver Awesome • HttpKernel • Console • Event Dispatcher • Filesystem Tools • Routing • Finder • CSSSelector • Locale • DomCrawler • Process • BrowserKit • Serializer • Config • Templating • Yaml • Form • DependencyInjection • Translation • Security • Validator @weaverryanFriday, September 28, 12
  • Now with more Windows Love! Process symfony/processFriday, September 28, 12
  • Find Symfony’s Process on Packagist.org { "require": { 1 "symfony/process": "2.1.x-dev" } } 2 php composer.phar install <?php // your app 3 require vendor/autoload.php; @weaverryanFriday, September 28, 12
  • External Script to Cook Dinner <?php // dinner.php for ($i = 1; $i <= 3; $i++) { sleep(1); echo sprintf("Cooking... %sn", $i); } echo "DING! n";Friday, September 28, 12
  • Run the script synchronouslyFriday, September 28, 12
  • // app.php require vendor/autoload.php; use SymfonyComponentProcessProcess; $process = new Process(php dinner.php); // if dinner takes longer than 5 seconds // to cook, freak out! $process->setTimeout(5); $process->run(function($type, $buffer) { echo $buffer; }); // executed after the command finishes if (!$process->isSuccessful()) { throw new Exception(Process misbehaved) }Friday, September 28, 12
  • Friday, September 28, 12
  • Run the script asynchronouslyFriday, September 28, 12
  • $process = new Process(php dinner.php); $process->start(); // do more work here echo "WORKING!!! n"; while ($process->isRunning()) { // wait for the process to finish }; echo $process->getOutput();Friday, September 28, 12
  • Interacting via StdinFriday, September 28, 12
  • $process = new Process(php dinner.php); $process->setStdin(turkey); “turkey” is read from StdinFriday, September 28, 12
  • Handling things that blow up! $process = new Process(php dinner.php); $process->run(function($type, $buffer) { if ($type == err) { // perhaps do some logging? echo $buffer; } }); if (!$process->isSuccessful()) { echo sprintf( "Process failed! Exit code %s, %sn", $process->getExitCode(), $process->getExitCodeText() ); }Friday, September 28, 12
  • Find out more about Process • Docs: http://bit.ly/sf2-process • Code: http://bit.ly/sf2-process-code • API: http://bit.ly/sf2-process-api @weaverryanFriday, September 28, 12
  • @kriswallsmith SPORK! $manager = new SporkProcessManager(); $manager->fork(function() { // do something in another process! return Hello from .getmypid(); })->then(function(SporkFork $fork) { // do something in the parent process afterwards echo "{$fork->getPid()} says {$fork->getResult()}"; }); Fork yourself some more PHP https://github.com/kriswallsmith/sporkFriday, September 28, 12
  • Finder symfony/finderFriday, September 28, 12
  • From the Fablog use SymfonyComponentsFinderFinder;   $s3 = new Zend_Service_Amazon_S3($key, $secret); $s3->registerStreamWrapper("s3");   $finder = new Finder(); $finder->name(photos*) ->size(< 100K) ->date(since 1 hour ago); foreach ($finder->in(s3://bucket-name) as $file) { // do something   print $file->getFilename()."n"; }   http://fabien.potencier.org/article/44/php-iterators-and- streams-are-awesomeFriday, September 28, 12
  • Find(er) out more about Finder • Docs: http://bit.ly/sf2-finder • Code: http://bit.ly/sf2-finder-code • API: http://bit.ly/sf2-finder-api @weaverryanFriday, September 28, 12
  • New in 2.1! Filesystem symfony/filesystemFriday, September 28, 12
  • Find Symfony’s Filesystem on Packagist.org { "require": { 1 "symfony/filesystem": "2.1.x-dev" } } 2 php composer.phar update symfony/filesystem <?php // your app 3 require vendor/autoload.php; @weaverryanFriday, September 28, 12
  • // ... use SymfonyComponentFilesystemFilesystem; $filesystem = new Filesystem(); $filesystem->copy( cowboy.jpg, s3://my-bucket/cowboy.jpg ); $filesystem->copy( s3://my-bucket/rodeo.mov, rodeo.mov ); $filesystem->mirror( thumbnails, s3://my-bucket/thumbnails );Friday, September 28, 12
  • // ... use SymfonyComponentFilesystemFilesystem; use SymfonyComponentFinderFinder; $finder = new Finder(); $finder->name(*.jpg) ->date(since 1 hour ago); ->in(thumbs); $filesystem = new Filesystem(); $filesystem->chmod($finder, 0777);Friday, September 28, 12
  • // ... use SymfonyComponentFilesystemFilesystem; $filesystem = new Filesystem(); $filesystem->touch(foo.txt); $filesystem->chmod(foo.txt, 0777); $filesystem->symlink(/some/dir, /symlink/target)Friday, September 28, 12
  • Find out more about Filesystem • Docs: http://bit.ly/sf2-filesystem • Code: http://bit.ly/sf2-filesystem-code • API: http://bit.ly/sf2-filesystem-api @weaverryanFriday, September 28, 12
  • Console symfony/consoleFriday, September 28, 12
  • Friday, September 28, 12
  • <?php // command.php $loader = require vendor/autoload.php; $loader->add(App, __DIR__); use SymfonyComponentConsoleApplication; use AppCommandEchoCommand; $app = new Application(); $app->add(new EchoCommand()); $app->run();Friday, September 28, 12
  • namespace AppCommand; use SymfonyComponentConsoleCommandCommand; use SymfonyComponentConsoleInputInputInterface; use SymfonyComponentConsoleOutputOutputInterface; class EchoCommand extends Command { protected function configure() { $this->setName(echo)->addArgument(msg); } public function execute( InputInterface $input, OutputInterface $output) { $output->writeln(Yo .$input->getArgument(msg)); } }Friday, September 28, 12
  • Output with pretty colorsFriday, September 28, 12
  • Find out more about Command • Docs http://bit.ly/sf2-console • Code http://bit.ly/sf2-console-code • API http://api.symfony.com/master/namespaces.html -> and search for “Console” @weaverryanFriday, September 28, 12
  • If you download Composer in the next 5 minutes, we’ll throw in a special gift!Friday, September 28, 12
  • CssSelector + DomCrawler symfony/css-selector symfony/dom-crawlerFriday, September 28, 12
  • require vendor/autoload.php; use SymfonyComponentDomCrawlerCrawler; $html = <<<HTML <!DOCTYPE html> <html> <body> <p class="message">Hello World!</p> <p>Hello Crawler!</p> </body> </html> HTML; $crawler = new Crawler($html); $crawler->filter(p)->eq(0)->attr(class); $crawler->filter(p)->eq(1)->text();Friday, September 28, 12
  • Goutte!Friday, September 28, 12
  • Config symfony/configFriday, September 28, 12
  • Config • Load configuration from many formats (YAML, PHP files, XML) • Config Validation • Caching for parsed config @weaverryanFriday, September 28, 12
  • Validator symfony/validatorFriday, September 28, 12
  • Shh... a little setupFriday, September 28, 12
  • require vendor/autoload.php; use SymfonyComponentValidatorValidator; use SymfonyComponentValidatorMappingClassMetadataFactory; use SymfonyComponentValidatorConstraintValidatorFactory; // a little setup $metadata = new ClassMetadataFactory(); $constraintFactory = new ConstraintValidatorFactory(); $validator = new Validator($metadata, $constraintFactory);Friday, September 28, 12
  • use SymfonyComponentValidatorConstraintsEmail; $value = ryan[AT]knplabs.com; $email = new Email(); $email->message = Invalid! You entered {{ value }}; $errors = $validator->validateValue($value, $email); if (count($errors) > 0) { echo strtr( $errors[0]->getMessageTemplate(), $errors[0]->getMessageParameters() ); }Friday, September 28, 12
  • 34 Core Validators and counting • String length See the “Reference” • Regular expressions section of the docs • Ip addresses • Dates • Collections • File size, mime-type, etc ... and the brand new Luhn validator for credit card numbers thanks to @merk -----------------------------------> @weaverryanFriday, September 28, 12
  • But wait there’s more!Friday, September 28, 12
  • More Components • DependencyInjection • Security • OptionsResolver • Locale • Serializer • Templating • Form • Translation @weaverryanFriday, September 28, 12
  • Many components are easyFriday, September 28, 12
  • Some are still wtf-hardFriday, September 28, 12
  • (cough) Security (cough)Friday, September 28, 12
  • Documentation Documentation DocumentationFriday, September 28, 12
  • The Security Component https://github.com/symfony/symfony-docs/pull/1604 WIP by @matthiasnobackFriday, September 28, 12
  • Act 3 Kicking ass in the new PHP eco-systemFriday, September 28, 12
  • The PHP Ecosphere Symfony is only one part of the picture • Symfony • Zend Framework • EZ Components • Drupal • ... • Individuals What other stuff exists? @weaverryanFriday, September 28, 12
  • How do we find good libraries?Friday, September 28, 12
  • Friday, September 28, 12
  • Friday, September 28, 12
  • Some FavoritesFriday, September 28, 12
  • Mink behat/mink http://mink.behat.org/ By @everzet, written on Fabien’s childhood computerFriday, September 28, 12
  • Command a browser, find elements, click them, and fill out forms$driver = new BehatMinkDriverSahiDriver(firefox);$session->visit(http://my_project.dev/some_page.php);$page = $session->getPage();$anchor = $page->find(css, .something);$anchor->click();// get the content of the new pageecho $page->getContent();Friday, September 28, 12
  • Monolog monolog/monolog https://github.com/Seldaek/monolog Your Friend JordiFriday, September 28, 12
  • A Logger, where bells and whistles come standard use MonologLogger; use MonologHandlerStreamHandler; use MonologHandlerFirePHPHandler; // Create the logger $logger = new Logger(my_logger); $logger->pushHandler(new StreamHandler( __DIR__./my_app.log )); $logger->pushHandler(new FirePHPHandler()); $logger->addInfo(My logger is now ready);Friday, September 28, 12
  • Zend Framework 2 They have some stuffFriday, September 28, 12
  • Yay Find More!Friday, September 28, 12
  • Epilogue 4 Reasons to get silly-excited about the FutureFriday, September 28, 12
  • 1) Look for Participation & ConsolidationFriday, September 28, 12
  • Shared Building-blocks • Drupal • phpBB • Midgard • Zikula • ez Publish 5 • ... @weaverryanFriday, September 28, 12
  • Less Library Duplication? • Zend/Form • Symfony/Form • Zend/Serializer • Symfony/Serializer • Zend/Http • Symfony/HttpFoundation • Zend/EventManager • Symfony/EventDispatcher • Zend/Log • Monlog • Zend/Navigation • KnpMenu • ... • ... @weaverryanFriday, September 28, 12
  • 2) More high quality, community-grown librariesFriday, September 28, 12
  • Solving 1 specific problem is a low barrier to entryFriday, September 28, 12
  • Find them, fork them.Friday, September 28, 12
  • Write their docs :)Friday, September 28, 12
  • Programmers that write docs get hugsFriday, September 28, 12
  • 3) Easier upgradesFriday, September 28, 12
  • 4) Grow your CommunityFriday, September 28, 12
  • Solutions exist outside of your frameworkFriday, September 28, 12
  • Fabien will not come to your house and tell you about themFriday, September 28, 12
  • Cast your vote by using the best libraries and improving themFriday, September 28, 12
  • and realize the power of the entire PHP communityFriday, September 28, 12
  • Thanks... Ryan Weaver @weaverryanFriday, September 28, 12
  • ... and we love you! https://joind.in/7218 Ryan Weaver @weaverryan Ryan Weaver @weaverryanFriday, September 28, 12