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