Building High Quality Composer Packages

By Korvin Szanto

Composer?

  • Manage dependencies
  • Distribute code
  • PSR-0 / PSR-4 autoloading

Dependencies

  • Versioned 
  • Automatically resolved
  • Automatically available to your code

Distribute code with Packagist

Autoloading

Composer handles all autoloading simply by including a single generated PHP file. 

<?php

require "./vendor/autoload.php";
  • PSR-4
  • PSR-0 (Deprecated)
  • Classmap
  • File
{
    "require": {
        "monolog/monolog": "1.0.*"
    },
    "autoload": {
        "psr-4": {
            "Vendor\\Package\\": "src/"
        }
    }
}

Do not rush into starting an open source project

NIH Syndrome

  • Spread too thin
  • Lack of real interest
  • Not contributing back to the ecosystem

Open Source is a Commitment

  • People will not help you, especially if you depend on them to.
  • If people use your software, they trust you to maintain it.
  • Unmaintained packages can be much more detrimental to the ecosystem than a lack of packages.

Search First

Another project probably already solved it better. If you find one, consider contributing instead of reinventing the wheel.

What does high quality look like?

  • Clear licensing
  • Code quality standards
  • Fully tested
  • Understandable version numbers
  • Clear documentation
  • Reasonable minimum requirements

Licensing

Use a standard license

  • MIT
  • GNU GPL v3
  • Apache 2.0

http://choosealicense.com/

Vanity License

  • WTFPL

http://choosealicense.com/

Everyone is permitted to copy and distribute verbatim or modified 
 copies of this license document, and changing it is allowed as long 
 as the name is changed. 

Closed Source Licensing

Closed source is cool too I guess, composer lets you provide "proprietary" for your license.

Code Quality

Why care?

  • Readable
  • Consistent
  • Clean pull requests
  • Maintain meaningful VCS history
<?php
(!is_numeric($input['sat_math']) && ($input['sat_math'] != "")) ? ($errors[] = "Your SAT math must be a numeric value, or blank.") : ('');
(!is_numeric($input['sat_verb']) && ($input['sat_verb'] != "")) ? ($errors[] = "Your SAT verbal must be a numeric value, or blank.") : ('');
(!is_numeric($input['sat_writ']) && ($input['sat_writ'] != "")) ? ($errors[] = "Your SAT writing must be a numeric value, or blank.") : ('');

((($input['act'] > 36) || ($input['act'] < 1)) && ($input['act'] != "")) ? ($errors[] = "Your ACT score is out of the valid range.") : ('');
((($input['act_eng'] > 36) || ($input['act_eng'] < 1)) && ($input['act_eng'] != "")) ? ($errors[] = "Your ACT English score is out of the valid range.") : ('');
((($input['act_math'] > 36) || ($input['act_math'] < 1)) && ($input['act_math'] != "")) ? ($errors[] = "Your ACT Math score is out of the valid range.") : ('');
((($input['act_read'] > 36) || ($input['act_read'] < 1)) && ($input['act_read'] != "")) ? ($errors[] = "Your ACT Reading score is out of the valid range.") : ('');
((($input['act_sci'] > 36) || ($input['act_sci'] < 1)) && ($input['act_sci'] != "")) ? ($errors[] = "Your ACT Science score is out of the valid range.") : ('');
((($input['sat_math'] > 800) || ($input['sat_math'] < 200)) && ($input['sat_math'] != "")) ? ($errors[] = "Your SAT math score is out of the valid range.") : ('');
((($input['sat_verb'] > 800) || ($input['sat_verb'] < 200)) && ($input['sat_verb'] != "")) ? ($errors[] = "Your SAT verbal score is out of the valid range.") : ('');
((($input['sat_writ'] > 800) || ($input['sat_writ'] < 200)) && ($input['sat_writ'] != "")) ? ($errors[] = "Your SAT writing score is out of the valid range.") : ('');

((($input['psat'] > 240) || ($input['psat'] < 60)) && ($input['psat'] != "")) ? ($errors[] = "Your PSAT is out of the valid range.") : ('');

(!array_key_exists($input['nat_merit'], $nationalMeritArray)) ? ($errors[] = "Please indicate whether or not you were awarded National Merit or National Achievement awards.") : ('');
(!array_key_exists($input['ap'], $apCountArray)) ? ($errors[] = "Please estimate how many APs your school offers.") : ('');

(!array_key_exists($input['hs_type'], $hs_type_array)) ? ($errors[] = "Please indicate what type of high school you attend (public, private, magnet, etc.).") : ('');
(!array_key_exists($input['hs_state'], $state_array)) ? ($errors[] = "Please indicate the state that your high school is in.") : ('');
($input['hs_city'] == "") ? ($errors[] = "Please indicate the city that your high school is in.") : ('');

(array_search($input['decile'], $decile_array) === false) ? ($errors[] = "Please indicate your class rank.") : ('');

((!is_numeric($input['class_size']) || $input['class_size'] < 1 || $input['class_size'] > 10000) && $input['class_size'] != "") ? ($errors[] = "If you indicate your class size, it must be numeric (without words, periods, or commas).") : ('');

Just steal a standard

Use PSR-1 and PSR-2 or steal the standard your favorite open source project follows.

It's not hard,

Let The Tools Do The Work

  • IDE
  • cs-fixer
  • styleci.io
  • Phabricator
  • git hooks

Testing

 

Write as you go

Google was not tested in a day.

 

Keep Them Fast

Nothing kills a Friday afternoon like slow tests

Achieve 100% Coverage

Regression Tests

If you find yourself saying

"Oh I remember fixing this issue last year.."

You should be using regression testing.

Tools To Use

  • Unit Testing: PHPUnit
  • Continuous integration: TravisCI or CircleCI
  • Code Coverage: coveralls.io

Versioning

SEMVER

  • Major: Incompatible API changes
  • Minor: API changes
  • Patch: Bug fixes

Major.Minor.Patch

Use Git Tags

Packagist keeps track of your git tags, use them to denote new versions

$ git commit -m "Release version 1.0.0"
$ git tag 1.0.0
$ git push

Stability Suffixes

  • 1.0.0-dev
  • 1.0.0-patch
  • 1.0.0-alpha
  • 1.0.0-beta
  • 1.0.0-RC

Documentation

Good Code Documents Itself

  • Keep your methods short and your complexity low
  • Use PHPDoc blocks
  • Keep your code clean and readable
  • Keep your tests up to date

Keep your Complexity Low

<?php

function foo()  {
    if ($a == $b)  {
        if ($a1 == $b1) {
            fiddle();
        } else if ($a2 == $b2) {
            fiddle();
        }  else {
            fiddle();
        }
    } else if ($c == $d) {
        while ($c == $d) {
            fiddle();
        }
    } else if ($e == $f) {
        for ($n = 0; $n < $h; $n++) {
            fiddle();
        }
    } else {
        switch ($z) {
            case 1:
                fiddle();
                break;
            case 2:
                fiddle();
                break;
            case 3:
                fiddle();
                break;
            default:
                fiddle();
                break;
        }
    }
}
<?php

function foo()  {
    if ($a == $b)  {
        do_a();
    } else if ($c == $d) {
        handle_b();
    } else if ($e == $f) {
        handle_c();
    } else {
        handle_d();
    }
}

Use PHPDoc Blocks

<?php

/**
 * Prepend a string with "Foo: "
 * 
 * ```
 * $result = foo("bar");
 * echo $result; // "Foo: bar"
 * ```
 * 
 * @param string $string
 * @return string The prepended string
 */
function foo($string) {
    return "Foo: {$string}";
}

It's Not Enough

Nothing beats natural language explanation.

Compare This...

To This

THANKS!

  • @korvinszanto
  • concretePHP