These days, good PHP object-oriented libraries are all around and easily available. To me, it is actually thrilling to be part of this flourishing community, while working with Symfony2 and blogging about the Framework, the Components and their neighbors (like Silex). It seems like everything is made for contributing to this nice and friendly environment, with tools like GitHub (online collaboration), Composer (dependency management), Packagist (package archive) and Travis CI (continuous integration).
Still, to me, contributing felt like too big a step to take right now. Until a few weeks ago, when I was looking for something I needed (a PHP client for the Microsoft Translator API) and could not find a decent solution. I decided to make it myself, and share it online. Below I've written down my steps. As you can see, they are very easy and would require just a bit of extra time. So, take from it what you need, and start contributing!
Write the code
First, write your software, but make it clean and clear: use not too long namespaces that speak for themselves. Remember, people will read this, and maybe even use this in production, so give it your best shot.
Initialize a Git repository
Once you have Git installed on your system, go to the root of your project's directory and run:
git init
This will initialize a Git repository. First add a .gitignore
file, to which you can later add directories and files that should not be under version control.
Add a composer.json file
Make sure your project has a composer.json file, in which you give it a name, write down who you are, and what its dependencies are. Composer downloads project dependencies into the /vendor
directory, and generates a file called autoload.php
. When you require
this file, you can start using your own classes and those in the vendor directory.
Don't forget to add vendor/*
and composer.lock
to the .gitignore
file.
Add unit tests
Write unit tests in the /tests
directory. Make them reflect the namespace hierarchy of your project's code: tests for Acme\Controller\BaseController
are to be found in the Acme\Tests\Controller\BaseControllerTest
class. Before running the tests, the class loader generated by Composer should be included. So create a bootstrap.php
file in your /tests
directory containing this code:
if (!is_file($autoloadFile = __DIR__.'/../vendor/autoload.php')) {
throw new \LogicException('Could not find autoload.php in vendor/. Did you run "composer install --dev"?');
}
require $autoloadFile;
Next, create a phpunit.xml.dist file in the root of your project. Make sure PHPUnit uses /tests/bootstrap.php
as a bootstrap file. Also point PHPUnit to the directory containing the tests. Add any PHP files that should be ignored when generating code coverage reports to the list of excludes:
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="./tests/bootstrap.php" colors="true">
<testsuites>
<testsuite name="Test suite">
<directory suffix="Test.php">./tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory>./</directory>
<exclude>
<directory>./tests</directory>
<directory>./vendor</directory>
</exclude>
</whitelist>
</filter>
</phpunit>
Allow developers who want to run the test suite with their own configuration to create a phpunit.xml
by adding "phpunit.xml" to .gitignore
.
Make it open source and developer friendly
Don't forget to add a LICENSE
file and a README.md
or README.rst
file (in Markdown or ReStructuredText). Below is the MIT
license, which is the standard for projects in the Symfony ecosystem:
Copyright (c) [year] [your name]
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
Push your code to GitHub
Once you have an account on GitHub, you can create a new repository there. Give it a nice name and don't let GitHub pre-fill it for you. Now, copy the URL of your repository.
In your project directory run:
git add remote origin <url-to-your-repository>
git push -u origin master
You will probably be asked to provide your GitHub credentials.
This effectively makes your project available to the world.
Register your project at packagist.org
To allow other developers to load your project as a dependency using Composer, you should register it as a package. Go to Packagist and log in (easiest would be using your GitHub account). Then, click on "Submit a package" and paste the URL of the GitHub repository.
Register the Packagist Service Hook
To make sure Packagist will be update the information about your package each time you push changes to your GitHub repository, you should register the Packagist Service Hook. Copy your API key from your account page on packagist.org. Then go to the homepage of your repository on GitHub and select to the "Admin" tab. Next, select "Service hooks" and scroll down to "Packagist". Click on the link, paste the API key in the designated field and click on "Update Settings".
Versioning
The above steps will have made your project available as a dependency for other projects using Composer. But people will have to "require" the "dev-master" version in their composer.json
file. This is not recommended, especially not for production software. You should give people the means to pick a stable version (by tagging it), or a stable branch, to which you will also apply any bug fixes. Branches also allow you to have different composer.json
files. For instance when you are working on a Symfony bundle, your project should have branches corresponding to the available Symfony versions. When you only have a master branch, which requires the FrameworkBundle's "dev-master" version, people won't be able to use your bundle in a Symfony 2.1 project, even if the code would work perfectly well.
Continuous integration using Travis CI
Travis CI will run all your unit tests for free for every commit to your project's repository, once your source code is out in the open. The only thing you need to do is add a .travis.yml
file to the root of your project containing a few lines:
language: php
php:
- 5.4
- 5.3
Travis allows you to log in using your GitHub account. Once you have done so, select the repository which should be continuously integrated by Travis. When it contains the .travis.yml
file above, Travis will run all the tests on PHP 5.3 and 5.4. For more options, see the Travis CI documentation for PHP.
You should also install the service hook, so builds will be run (almost) immediately after you have pushed changes to your repository.
Probably skip some unit tests
One last suggestion: you will find that Travis will not be able to all your (be it sometimes slightly exotic) tests. This does not need to make your build fail. For example, when testing an external API, you should skip the functional tests making the real calls and only run true unit tests. One way to do this, is by using environment variables. Your test might look like this:
class ApiFunctionalTest extends \PHPUnit_Framework_TestCase
{
public function testCall()
{
if (!isset($_ENV['API_KEY'])) {
$this->markTestSkipped('No API key available');
}
// otherwise, just go!
}
}
On your local machine, you can copy phpunit.xml.dist
to phpunit.xml
(and since you added this file to .gitignore
it will not be under version control). Then add these lines:
<phpunit>
<php>
<env name="API_KEY" value="my-secret-api-key" />
</php>
</phpunit>
Now, when you run the functional test again, it will not skip the test. But when Travis runs the tests, the environment variable "API_KEY" will not be available, and the test will be skipped.
By the way, to help you build a reputation (though its result can be the opposite), you can add the current Travis build status as an image to for instance your project's README
file, using one of the formats Travis supplies.
This is exactly the fourth post, of your blog I browsed.
However , I really like this 1, “Experiences with PHP Open Source Software in
a Symfony-Friendly Environment | PHP & Symfony” the
best. Regards ,Woodrow
Thank you :)
Nice Travis CI thing..
I like the unit tests for every commit .. But is it should be pass the test to approve the commit? .. if it's like that it's so fantastic!
Thanks!
I think Gerrit is what you need.
Hi Saud, Travis CI runs unit tests for code that has already been pushed to the remote server. When you want to test your code before a commit is "approved", you should do try to set up a pre-commit hook on your own machine. I have no experience with this, but it exists.
Such a good article. I particularly loved the part about phpunit ENVs, it was new to me and I learned something truly useful.
You should also explicitly mention one of the most important part of creating open source code: "writing documentation"!
I do really love the approach of Tom Preston-Werner (github cofounder) of writing the documentation first: Readme driven development ( http://tom.preston-werner.c... ).
why not just including here the autoload from composer? saves you creating a file
also on branches you should have just tag it
why did you need microsoft translator? why not google?
Using
bootstrap.php
and throwing an exception is just a bit of friendliness. Also, it allows you to configure anything else that's necessary for running tests. Microsoft Translator has a free plan (up to 2 million characters), Google has not.You can't add the composer.lock to your .gitignore file, This file lock the vendors version.
You need it if you want to share a working library
Thanks for the tutorial !
Exactly what I did for my new Taleo PHP Library !
See https://github.com/Polzme/T...