PHP SA 2014 - Releasing Your Open Source Project

1,241 views
1,090 views

Published on

Learn about how to write open source libraries people won't (completely) hate.

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
1,241
On SlideShare
0
From Embeds
0
Number of Embeds
34
Actions
Shares
0
Downloads
8
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

PHP SA 2014 - Releasing Your Open Source Project

  1. 1. Releasing Your Open Source Project How to write libraries people won't (completely) hate By @thomas_shone
  2. 2. WARNING ● Opinionated ● Rambling ● Foul-Mouthed ● Ugly Slides ● Don't feed after midnight
  3. 3. 00. Introduction ● No formal education in PHP ● Learnt PHP the hard way ● This talk is about the process ● Connect with personalities
  4. 4. 00. Introduction
  5. 5. 00. Introduction <?php function isUrlSafe($api_key, $url) { if (!filter_var($url, FILTER_VALIDATE_URL)) { throw new Exception('Invalid URL specified.') } $api_uri = 'https://sb-ssl.google.com/' . 'safebrowsing/api/lookup?client=api&apikey=' . $api_key . '&appver=1.0&pver=3.0&url=' . urlencode($url); $result = file_get_contents($api_uri); return strpos($result, 'malware') === false && strpos($result, 'phishing') === false; } When you've finished reading this code, touch your nose so I know when everyone is done.
  6. 6. 10. Security ● Shouldn't be left till last ● Unsure of how to do it properly ● Worst thing that can happen to insecure open source code is it becomes popular
  7. 7. 10. Security ● Shouldn't be left till last ● Unsure of how to do it properly ● Worst thing that can happen to insecure open source code is it becomes popular
  8. 8. 01. Security ● Shouldn't be left till last ● Unsure of how to do it properly ● Worst thing that can happen to insecure open source code is it becomes popular
  9. 9. 01. Security ● Joomla! – 5/133 versions secure – 85 vulnerabilities – 0/322 of scanned sites secured ● WordPress – 2/322 versions secure – 54 vulnerabilities – 313/589 of scanned sites secure
  10. 10. 01. Secure Communication What questions do we need to ask to ensure our communication is secure?
  11. 11. 01. Secure Communication 1. Can C overhear what A and B are saying? BA C
  12. 12. 01. Secure Communication 2. Is A sure s/he is talking to B and C isn't standing in the middle? BA C
  13. 13. 01. Secure Communication 3. Does A trust B not to tell C? BA C
  14. 14. 01. Secure Communication ● Certificate file sourced from https://github.com/guzzle/guzzle/blob/master/src/cacert.pem ● disable_compression only available in PHP 5.4.13+ (prevents CRIME/BREACH attacks) ● Not required with PHP 5.6+ thanks to this guy... $context = stream_context_create([ 'ssl' => [ 'verify_peer' => true, 'verify_depth' => 5, 'cafile' => 'cacert.pem', 'CN_match' => 'sb-ssl.google.com', 'disable_compression' => true, ] ]); $result = file_get_contents($api_uri, false, $context);
  15. 15. 01. Personality Daniel Lowrey (@rdlowrey) PHP SSL/TLS Contributor Saving us from ourselves Itotallygotpermissiontousethisphoto
  16. 16. 02. Hosting ● phpclasses.org ● sourceforge.net ● pear.php.net ● bitbucket.org ● github.com
  17. 17. 02. Hosting ● phpclasses.org - FUCK NO! ● sourceforge.net - HELL NO! ● pear.php.net - No ● bitbucket.org - No ● github.com - Yes
  18. 18. 03. Managing your source ● Source control (already determined) ● Version ● License
  19. 19. 03. Credentials ● .gitignore to exclude sensitive data ● If you've pushed sensitive data to github, change your credentials asap https://help.github.com/articles/remove-sensitive-data Don't be that guy
  20. 20. 03. Licensing ● MIT – Do whatever you want with it – Must attribute – Don't blame me if it causes a zombie outbreak ● Apache – Same as MIT – contributors grants patent rights to users ● GPL – Must release any changes or improvements – Can't change license – Ditto with the zombies http://choosealicense.com/
  21. 21. 03. Versioning MAJOR.MINOR.PATCH-STABILITY ● Breaking backward compatibility? Increase MAJOR ● Adding backwards compatible feature? Increase MINOR ● Adding bugfix? Increase PATCH ● Not production ready? Add stability value (alpha, beta, preview) http://semver.org/
  22. 22. 04. Package Management
  23. 23. 04. Package Management ● PEAR – No space for alternatives – High requirement levels – Package signing ● Composer – Easy to install/update dependencies – Version locking – Autoloading – Your package becomes smaller – Package signing (almost) – Doubles as a distribution platform (https://packagist.org )
  24. 24. 04. Package Management ● PEAR - NO – No space for alternatives – High requirement levels – Package signing ● Composer - YES – Easy to install/update dependencies – Version locking – Autoloading – Your package becomes smaller – Package signing (almost) – Doubles as a distribution platform (https://packagist.org )
  25. 25. 04. Composer $ mkdir safebrowser && cd safebrowser $ curl -s http://getcomposer.org/installer | php #!/usr/bin/env php All settings correct for using Composer Downloading... Composer successfully installed to: /home/project/composer.phar Use it: php composer.phar
  26. 26. 04. Composer $ php composer.phar init Welcome to the Composer config generator This command will guide you through creating your composer.json config. Package name (<vendor>/<name>) [thomas/project]:xsist10/SafeBrowsing Description []: Google Safe Browsing Client Author [Thomas Shone <xsist10@gmail.com>]: Minimum Stability []: License []: MIT
  27. 27. 04. Composer { "name": "xsist10/safebrowser", "description": "Google SafeBrowser Client", "license": "MIT", "authors": [ { "name": "Thomas Shone", "email": "xsist10@gmail.com" } ], "require": { }, "autoload": { "psr-4": { "xsist10SafeBrowsing": "src/" } } }
  28. 28. 04. Composer $ php composer.phar install Loading composer repositories with package information Installing dependencies (including require-dev) Nothing to install or update Generating autoload files $ vi index.php <?php require 'vendor/autoload.php';
  29. 29. 04. Don't commit vendor/ # Your code src/[Your Library] vendor/[Dependencies] $ echo "vendor" >> .gitignore # Someone using your library src/[Their Project Code] vendor/xsist10/SafeBrowsing/[Your Library] vendor/[Your Library Dependencies] vendor/[Their Dependencies] Some of these might be the same # You don't want this vendor/xsist10/SafeBrowsing/[Your Library]/vendor/
  30. 30. 04. Composer $ mkdir src $ vi src/SafeBrowsing.php <?php namespace xsist10SafeBrowsing; class SafeBrowsing { public function __construct($api_key) { $this->api_key = $api_key; } public function isUrlSafe($url) { // ... } }
  31. 31. 04. Composer <?php require 'vendor/autoload.php'; use xsist10SafeBrowsingSafeBrowsing; $safeBrowsing = new SafeBrowsing($api_key); $safeBrowsing->isUrlSafe('www.example.com');
  32. 32. 04. List on Packagist
  33. 33. 04. List on Packagist $ php composer.phar require xsist10/safebrowser=dev-master
  34. 34. 04. Setup Webhook
  35. 35. 04. Setup Webhook
  36. 36. 04. Setup Webhook
  37. 37. 04. Setup Webhook
  38. 38. 04. Package Signing ● Currently being implemented in Composer – https://github.com/composer/composer/pull/2814 ● Ensure that the package you're installing hasn't been tampered with, like: – Ruby Gem installs – PEAR libraries – Linux packages (deb, rpm, yum) – Windows binaries
  39. 39. 04. Package Signing # When you first setup your project $ php composer.phar create-keys --directory /path/ --prefix=mykey --passphrase passphrase to encrypt the private key: $ php composer.phar add-dev-key /path/mykey-private.pem $ php composer.phar sign-dev-keys /path/mykey-private.pem # Last thing you do before you release a new version $ php composer.phar sign /path/mykey-private.pem Enter a passphrase if the private key is encrypted: $ git commit -m “Updated keys” keys.json manifest.json $ git push # Tag you release immediately
  40. 40. 04. Version
  41. 41. 04. Personality Pádraic Brady (@padraicb) Zend Framework / Composer contributor Working on the signing code Thisguyissoawesomethattheinternetcan't containpicturesofhim.
  42. 42. 05. Design Patterns ● Increase flexibility without having to modify the library code ● Provide rules on how to extend
  43. 43. 05. Strategy
  44. 44. 05. Strategy <?php namespace xsist10SafeBrowsingStrategy; interface Strategy { public function execute($url, $param); } https://en.wikipedia.org/wiki/Strategy_pattern
  45. 45. 05. Strategy <?php namespace xsist10SafeBrowsingStrategy; class Get implements Strategy { public function execute($url, $param) { $context = stream_context_create([ 'ssl' => [ 'verify_peer' => true, 'cafile' => 'path/to/cafile', 'CN_match' => 'sb-ssl.google.com' ] ]); $query = $url . '?' . http_build_query($param); return file_get_contents($query, false, $context); } }
  46. 46. 05. Strategy <?php namespace xsist10SafeBrowsingStrategy; class Post implements Strategy { public function execute($url, $param) { // Do some curl init stuff ... // Do your security! curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 1); curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2); curl_setopt($curl, CURLOPT_CAINFO, 'path/to/cafile'); $result = curl_exec($curl); $code = curl_getinfo($curl, CURLINFO_HTTP_CODE); curl_close($curl); // check some result checks first ... return $result; } }
  47. 47. 05. Strategy <?php require 'vendor/autoload.php'; use xsist10SafeBrowsingSafeBrowsing; use xsist10SafeBrowsingStrategyGet; $sb = new SafeBrowsing($api_key, new Get()); $sb->isUrlSafe('www.example.com'); use xsist10SafeBrowsingStrategyPost; $sb = new SafeBrowsing($api_key, new Post()); $sb->isUrlSafe('www.example.com');
  48. 48. 05. Chain of Responsibility
  49. 49. 05. Chain of Responsibility <?php namespace xsist10SafeBrowsingStrategy; use Exception; class UnavailableException extends Exception {} https://en.wikipedia.org/wiki/Strategy_pattern
  50. 50. 05. Chain of Responsibility <?php namespace xsist10SafeBrowsingStrategy; class Get implements Strategy { public function execute($url, $param) { if (!ini_get('allow_url_fopen')) { throw new UnavailableException(); } // ... } }
  51. 51. 05. Chain of Responsibility <?php namespace xsist10SafeBrowsingStrategy; class Post implements Strategy { public function execute($url, $param) { if (!function_exists('curl_init')) { throw new UnavailableException(); } // ... } }
  52. 52. 05. Chain of Responsibility <?php namespace xsist10SafeBrowsing; use xsist10SafeBrowsingStrategyStrategy; class Chain implements Strategy { public function append(Strategy $strat) { $this->chain[] = $strat; } public function execute($url, $param) { foreach ($this->chain as $strategy) { try { return $strategy->get($url, $param); } catch (UnavailableException $exception) { // We can ignore and move to the next } } throw new Exception('No available strategy.'); } }
  53. 53. 05. Put the chain links together <?php // ... use xsist10SafeBrowsingChain; $chain = new Chain(); $chain->append(new Post()); $chain->append(new Get()); $sb = new SafeBrowsing($api_key, $chain); $sb->isUrlSafe('www.example.com'); // This still works $sb = new SafeBrowsing($api_key, new Get()); $sb->isUrlSafe('www.example.com');
  54. 54. 05. The start of something beautiful <?php // ... use SomeOtherGuySomeOtherPackageCache; $chain = new Chain(); $chain->append(new Cache()); $chain->append(new Post()); $chain->append(new Get()); $sb = new SafeBrowsing($api_key, $chain); $sb->isUrlSafe('www.example.com');
  55. 55. 05. Personality Martin Fowler (@martinfowler) Design Pattern Tamer http://www.martinfowler.com/ Hegetssuperpowersfromhishat
  56. 56. So what next? Shamelessly copied from http://thephpleague.com/ ● Follow PSR-2, we use League as our PSR-0 namespace. ● List on Packagist, we list with league as the vendor namespace. ● Shove code in a src folder. ● Write unit tests. Aim for at least 80% coverage for v1.0. ● DocBlock all the things. ● Semantic versioning must be used to manage version numbers. ● Use Travis-CI to automatically check coding standards and run tests. ● Have an extensive README.
  57. 57. 06. Why Tests? ● You will always find bugs ● Confidence in libraries ● Prevent regressions ● Ensure new features have been thoroughly vetted
  58. 58. 06. PHPUnit $ php composer.phar require --dev phpunit/phpunit=4.0.*@dev ./composer.json has been updated Loading composer repositories with package information Updating dependencies (including require-dev) ... - Installing phpunit/phpunit (4.0.x-dev fca5bc6) Cloning fca5bc6a50d09b26db280c5cc3c84978c9cace3f phpunit/phpunit suggests installing phpunit/php-invoker (~1.1) Writing lock file Generating autoload files
  59. 59. 06. PHPUnit $ vi phpunit.xml <?xml version="1.0" encoding="UTF-8"?> <phpunit backupGlobals="false" convertErrorsToExceptions="true" convertWarningsToExceptions="true" convertNoticesToExceptions="true" mapTestClassNameToCoveredClassName="true" bootstrap="vendor/autoload.php" strict="true" verbose="true" colors="true"> <testsuites> <testsuite> <directory>./tests</directory> </testsuite> </testsuites> </phpunit> $ mkdir tests $ vi phpunit.xml
  60. 60. 06. PHPUnit $ vi phpunit.xml$ ./vendor/bin/phpunit PHPUnit 4.0.13 by Sebastian Bergmann. Configuration read from /path/to/project/phpunit.xml Time: 116 ms, Memory: 2.00Mb No tests executed!
  61. 61. 06. First Tests use xsist10SafeBrowsingSafeBrowsing; use xsist10SafeBrowsingStrategyChain; class SafeBrowsingTest extends PHPUnit_Framework_TestCase { public function testInvalidUrl() { $chain = new Chain(); $safeBrowsing = new SafeBrowsing('', $chain); $message = 'Invalid URL specified.'; $this->setExpectedException('Exception', $message); $safeBrowsing->isUrlSafe('invalid-url'); } } $ vi tests/SafeBrowsingTest.php
  62. 62. 06. First Tests public function testSecure() { $mock = $this->getMockBuilder( 'xsist10SafeBrowsingStrategyChain', ['execute'] )->getMock(); // API returns an empty result if the site is secure $mock->expects($this->once()) ->method('execute') ->will($this->returnValue('')); $safeBrowsing = new SafeBrowsing('', $mock); $url = 'http://www.google.com'; $response = $safeBrowsing->isUrlSafe($url); $this->assertTrue($response); }
  63. 63. 06. First Tests $ vi phpunit.xml$ ./vendor/bin/phpunit PHPUnit 4.0.13 by Sebastian Bergmann. Configuration read from /path/to/project/phpunit.xml ..... Time: 568 ms, Memory: 4.00Mb OK (5 tests, 9 assertions)
  64. 64. 06. Testing Resources ● Can't mock out resources ● Wrap resources in class and mock the class ● Wait! Don't write from scratch. Use your package manager!
  65. 65. 06. cURL wrapper $ ./composer.phar search curl ext-curl The curl PHP extension lib-curl The curl PHP library kdyby/curl Curl wrapper for Nette Framework shuber/curl PHP Wrapper for Curl comvi/curl Work with remote servers via cURL much easier than using the native PHP bindings. anlutro/curl Simple OOP cURL wrapper. jyggen/curl A simple and lightweight cURL library with support for multiple requests in parallel. bca/curl cURL wrapper for PHP applications. unikent/curl Laravel Curl Helper Library. mogetutu/curl Simple Curl PHP Helper Library sweelix/curl PHP 5.3+ simple curl requestor lib/curl A simple cURL wrapper for PHP dvelopment/curl PHP OOP wrapper for cURL requests php-curl-class/php-curl-class PHP Curl Class is an object-oriented wrapper of the PHP cURL extension.
  66. 66. 06. cURL wrapper $ php composer.phar require shuber/curl=dev-master ./composer.json has been updated Loading composer repositories with package information Updating dependencies (including require-dev) - Installing shuber/curl (dev-master 6624992) Cloning 6624992df201f9fd7262080117385dd09b0ecd2b Writing lock file Generating autoload files
  67. 67. 06. Personality Chris Hartjes (@grmpyprogrammer) Testing advocate Being grumpy... so we don't have to Itotallydidn'tgetpermissiontousethisphoto
  68. 68. 06. Personality Howhereactstoalackoftests.
  69. 69. 07. Code Coverage ● Ensure you test all use cases ● Useful to spot code smell ● Helpful in identifying dead/unreachable code ● Improves confidence in library
  70. 70. 07. Coverage Report <phpunit ...> ... <logging> <log type="coverage-html" target="build/report" charset="UTF-8" highlight="false" LowUpperBound="35" HighLowerBound="70" /> </logging> <filter> <whitelist> <directory>src</directory> </whitelist> </filter> </phpunit> $ echo “build” >> .gitignore $ vi phpunit.xml
  71. 71. 07. Coverage Report
  72. 72. 07. Coverage Report
  73. 73. 07. Coverage Report
  74. 74. 07. Ignore coverage ● Ignore whole class/function – @codeCoverageIgnore ● Ignore certain lines of code – // @codeCoverageIgnoreStart – // @codeCoverageIgnoreEnd ● Use responsibly
  75. 75. 08. Continuous Integration ● Make sure your development branch is always in a deployable state. ● Ingredients: Tests, High Coverage, Automation
  76. 76. 08. Travis-CI language: php before_script: - wget http://getcomposer.org/composer.phar - php composer.phar install --dev php: - 5.5 - 5.4 - hhvm script: phpunit $ vi .travis.yml
  77. 77. 08. CLI Tools ● Copy paste detector ● Code Sniffer ● Mess Detector ● And lots more at http://phpqatools.org/
  78. 78. 07 – Copy/paste detector $ php composer.phar require --dev sebastian/phpcpd=dev-master ./composer.json has been updated Loading composer repositories with package information Updating dependencies (including require-dev) - Installing sebastian/phpcpd (dev-master a946215) Cloning a9462153f2dd90466a010179901d31fbff598365 Writing lock file Generating autoload files $ ./vendor/bin/phpcpd src/ phpcpd 2.0.1 by Sebastian Bergmann. 0.00% duplicated lines out of 195 total lines of code. Time: 32 ms, Memory: 2.75Mb
  79. 79. 08. Code Sniffer $ php composer.phar require --dev squizlabs/php_codesniffer=dev-master ./composer.json has been updated Loading composer repositories with package information Updating dependencies (including require-dev) - Installing squizlabs/php_codesniffer (dev-master 623905c) Cloning 623905ce571d64a8cb873826d47b81321cd55011 Writing lock file Generating autoload files $ ./vendor/bin/phpcs -i The installed coding standards are PSR1, PHPCS, Squiz, PEAR, Zend, MySource and PSR2 $ ./vendor/bin/phpcs --standard=PSR2 src/ [WALL OF TEXT]
  80. 80. 08. Select a Standard $ ./vendor/bin/phpcs --standard=PSR2 src/ FILE: /path/to/project/SafeBrowsing/src/SafeBrowsing.php ------------------------------------------------------------------- FOUND 3 ERROR(S) AFFECTING 2 LINE(S) ------------------------------------------------------------------- 17 | ERROR | Opening brace should be on a new line 22 | ERROR | Visibility must be declared on method "isUrlSafe" 22 | ERROR | Opening brace should be on a new line ------------------------------------------------------------------- [WALL OF TEXT OMITTED]
  81. 81. 08. Custom Standard $ ./vendor/bin/phpcs --standard=/path/to/own/standard src/ FILE: /path/to/project/SafeBrowsing/src/SafeBrowsing.php ------------------------------------------------------------------- FOUND 1 ERROR(S) AFFECTING 1 LINE(S) ------------------------------------------------------------------- 1 | ERROR | Homage to Cthulhu missing from doc header -------------------------------------------------------------------
  82. 82. 08. Mess Detector $ php composer.phar require --dev phpmd/phpmd=1.4.* ./composer.json has been updated Loading composer repositories with package information Updating dependencies (including require-dev) - Installing phpmd/phpmd (1.4.1) Downloading: 100% Writing lock file Generating autoload files $ ./vendor/bin/phpmd src text codesize,unusedcode,naming,design Strategy/Get.php:9 Classes should not have a constructor method with the same name as the class
  83. 83. 08. Taking it further ● Jenkins – http://jenkins-ci.org/ – Automate all the things ● Behat – http://behat.org/ – Cucumber syntax – Mink extension for website testing – Write human-readable use cases
  84. 84. 08. Behat and Mink Feature: Test the login page In order to ensure that customer can use our system I need to make sure that they can log in successfully Scenario: Can I log in with valid details Given I am on the “www.mywebsite.com” When I click on “Login” And I fill “username” with “bob” And I fill “password” with “Password1” And I press “Login” Then I should see “Login Successful”
  85. 85. 09. Flair ● General badges (versions, license, etc) – https://poser.pugx.org/ ● Build status – https://travis-ci.org ● Code Coverage – https://coveralls.io ● Code Analysis – https://insight.sensiolabs.com/analyses – https://scrutinizer-ci.com/
  86. 86. 10. Engage ● Write a useful README.md – First thing you see on Github – How to install – How to use
  87. 87. 10. Engage with developers ● Encourage fork/pull requests – Make sure they add tests – Make sure the old tests still pass – Travis-CI makes this simple
  88. 88. 10. Engage with developers ● Promote your library – Twitter? – Google Plus? – I have no idea. I'm still figuring this out. I'm a developer dammit!
  89. 89. Homework ● Docblocks ● phing/ant to automate CLI tools ● Git pre-commit hooks to run tests
  90. 90. Social Awareness Too many pasty white guys @phpwomen http://phpwomen.org/
  91. 91. Questions? Twitter: @thomas_shone Github: https://github.com/xsist10 https://github.com/xsist10/SafeBrowsing

×