If you've been wanting to contribute to PHP internals, starting with the documentation can be a great entry point; especially because it doesn't require dusting off those old C books from college.
But knowing where to start can be tricky since information on how to contribute to the docs is scattered across the internet.
This article is a step-by-step guide of how to contribute documentation to the PHP manual. My hope is that you can use this guide to:
- Write documentation for extensions that don't have any documentation yet
- Fix type-o's or add more documentation to existing docs
The quick & dirty way
There is a quick & dirty way to edit the docs. You can log into edit.php.net with your GitHub or Facebook account and see the documentation repo in a GUI.
If you click on "All Files" and toggle the "Repository" folder, you'll be able to browse and edit all the doc files. This can be handy for quick & dirty edits. All your changes will be reviewed by someone with proper permission ("docs karma") to commit them.
But if you're going to be making some bigger contributions to the PHP documentation manual, you'll want to set up a proper documentation contribution environment on your machine.
Before we begin, I'm assuming:
- You're running an OS X/Linux environment
- You've got php installed & can run it from the CLI
- You have git and subversion installed
We'll make a directory were we can put all the tools & repos we'll need to start contributing to the docs.
$ mkdir ~/php-docs $ cd ~/php-docs
Now that we're
cd'ed into the
php-docs directory, we need to download some stuff.
The documentation repo
We'll need to clone the documentation repo down to our machine. So from the
$ svn co https://svn.php.net/repository/phpdoc/modules/doc-en doc-en $ cd ./doc-en
You get two repos! When you check out the
repository/phpdoc/modules/*repo, it will download two separate repos for you. One repo will be in
doc-baseand the other will be in
en. When you make commits to either repo, you'll need to do a
svn commitfrom the correct directory.
If you want to pull down a different language, just use the two-letter country code in place of
en. For example, to download the French docs:
$ svn co https://svn.php.net/repository/phpdoc/modules/doc-fr doc-fr $ cd ./doc-fr
The DocBook format
If you poke around the repo, you'll noticed a bunch of weird
.xml files. These are the raw docs files before they are built into html or php. The XML format is DocBook. You have to keep in mind that the PHP docs were implemented way before markdown was the new hotness.
The reference docs
The main docs for all the functions/methods/etc. are found in the
/en/reference/ directory. Each extension has its own directory. You'll noticed for each extension, they all follow a certain file structure.
Check out some of the files to familiarize yourself with the file structure and DocBook syntax.
PhD: The PHP docs generator
So how does one convert all these DocBook XML files into something useful? With a tool called PhD. Let's download it.
$ cd ~/php-docs $ git clone http://git.php.net/repository/phd.git $ cd ./phd
Make sure you can see the help output.
$ php render.php --help
Validate the DocBook XML
Because XML is overly verbose, easy to screw up, & just properly awful, we need to make sure that the XML is valid in the eyes of the computer. The
doc-base repo has a script for validating the XML & generating some cache files.
It's a good idea to run this before you start mucking around with the XML files since you'll probably break something like I did.
$ cd ~/php-docs/doc-en $ php doc-base/configure.php
If you see a bunch of output that ends with an ASCII cat picture, you're in business.
Build the docs
Now that we have validated the XML files and generated some config cache we can use PhD to generate the php files that will be used in the php.net site. For this example we're going to generate the files in the
~/php-docs/rendered-docs directory. This directory will be created for us when we run the generator.
$ cd ~/php-docs/phd $ php render.php --docbook ~/php-docs/doc-en/doc-base/.manual.xml --package PHP --format php --output ~/php-docs/rendered-docs
This should take some time to generate all the docs (there's a lot of crap in PHP), but once it's finished, you should see a bunch of output with the last line being:
[16:30:49 - Rendering Format ] Finished rendering
Yay! The docs have been converted from DocBook XML files to
.php files. But you might be asking yourself, "How do I see a preview of all this stuff?" The answer is to download the php.net site and run it from your machine.
Download the php.net site
The final step in getting our environment set up to contribute to the PHP documentation site, is to download & run php.net on your local machine.
The php.net site is version controlled with git so you can clone the repo to your machine.
$ cd ~/php-docs/ $ git clone http://git.php.net/repository/web/php.git php.net
The repo comes with a bare-bones version of the reference docs. So we need to delete the bare-bones version and create a symbolic link to our generated docs.
$ cd ~/php-docs/php.net/manual $ rm -Rf en/ $ ln -s ~/php-docs/rendered-docs/php-web en
The final step is to run a web server from the root
php.net repo and browse the site.
$ cd ~/php-docs/php.net/ $ php -S 0.0.0.0:4000
Now in your browser go to
http://localhost:4000/ and you should see a fully-functional php.net site running on your machine. Yay!
Make some changes
Now that you have all the tools installed, you can iterate through some changes relatively quickly. Make some changes to one of the existing extensions in
~/php-docs/doc-en/en/reference/ and regenerate the docs.
$ php ~/php-docs/doc-en/doc-base/configure.php $ php ~/php-docs/phd/render.php --docbook ~/php-docs/doc-en/doc-base/.manual.xml --package PHP --format php --output ~/php-docs/rendered-docs
Then refresh the corresponding page in your local mirror of php.net (
http://localhost:4000/) and you should see the changes.
Make sure you've familiarized yourself with some guidelines for adding docs in the PHP docs style guide.
Writing extension documentation from scratch
There are two ways to write new documentation from scratch: 1) using a skeleton file, 2) using the
Using skeleton files
The skeleton files are located in the docs repo under
~/php-docs/doc-en/doc-base/RFC/skeletons. You'll notice a number of default DocBook XML files there for classes, functions, methods & lots more.
You can simply copy & paste (something very familiar to us PHP developers) those files into an existing extension folder or create your own extension folder following the file structure. This was the way I added the CSPRNG documentation since I co-authored
random_bytes() in PHP7.
Using the docgen script
When an extension has no documentation associated with it, you can use the
docgen script found in the docs repo under
If our extension was called
foobar, we could generate the DocBook XML files with the following command.
$ cd ~/php-docs/doc-en/doc-base/scripts/docgen/ $ php docgen.php -e foobar -o ~/php-docs/en/reference
You can see all the commands available in
$ php docgen.php -h
Adding the extension to the function reference
After you've added all the docs for the extension, you'll need to edit the
~/php-docs/doc-en/doc-base/manual.xml.in and place your extension under the appropriate category. This will make sure it shows up under the function reference and will give it a pretty breadcrumb nav at the top of each function/method doc page.
Submitting your changes
PHP internals works on a karma system. You can't commit your changes to the SVN docs repo unless you have the karma to do so.
If you're just making a few quick changes via the edit.php.net site, then you can get by with not having "docs karma". But if you wanted to make more significant modifications to the docs, you'll need to get karma.
Getting docs karma
How do you get karma? That's really the million-dollar tribal-knowledge-only question.
I tried to get it directly from @derickr via twitter...
...which turned out to be the wrong way.
Submit a formal request
To get docs karma, you'll need to fill out the request-git-account form and then do some follow-up. The request form is the official way to gain access to the php repos for both svn & git. If you are approved, you'll be granted an
@php.net account with permission to commit to the docs repos.
Follow up the formal request with reasons
But the php karma lords aren't handing out docs karma willy-nilly. You'll need to have some good reasons for them to approve you. So you'll want to follow up your formal request with the doc karma lords via the firstname.lastname@example.org mailing list. Let them know, "Hey can you give me docs karma for x & y reasons..." Or even better, if you've made docs contributions via the quick-and-dirty way, then mention those or any other ways you've contributed to internals.
If you haven't contributed anything yet, tell them you've set up all the docs repos locally and have made x & y contributions and send some screenshots of the work you're trying to submit like I did.
Not only did I show them all the work I had done on the docs, but I've meet a lot of internals folks in person at a number of PHP conferences, and some of them knew me from my CSPRNG RFC, and others saw all the stupid questions I would ask in the stackoverflow PHP chatroom about php-src. So really the moral of the story here is to get involved in the PHP community if you haven't already.
You can also follow up with your formal request for docs karma in the
#php.doc IRC channel on Efnet. Although I don't use IRC so I know not about such things. Apparently lots of internals folks like IRC.
Not all karma's are the same. If you'd like to add a feature to PHP, you'll need to go through the RFC process. Before you can create an RFC, you'll need RFC karma. RFC karma does not require you have a
@php.netaccount but you will need a wiki account. Then you can request RFC karma for your account via the php internals list like I did. See, "how to create an RFC" for more info.
Submitting your changes
Once you have sufficient karma, you can push your changes up to the SVN repo for review.
The $Revision: number
By now you've probably noticed this line at the top of nearly every DocBook XML file:
<!-- $Revision: 299488 $ -->
Those are svn keywords that get updated automatically when you commit changes. They are used in the PHP docs project to keep track of which files need to have their corresponding translations updated.
SVN keywords have have to be explicitly enabled on your system. So before you commit anything, you'll want to edit your SVN config file
~/.subversion/config with your favorite editor in order to enable the keywords used by the PHP docs.
$ vi ~/.subversion/config
Look for the section
[auto-props] (mine was at the bottom of the file) and add the following line:
*.xml = svn:eol-style=native;svn:keywords=Id Rev Revision Date LastChangedDate LastChangedRevision Author LastChangedBy HeadURL URL
Add your files and commit
Remember that when you checked out the docs repo, it checked out two separate repos for you - the base docs repo (
doc-base) and the language repo (
en). You'll need to
cd into each one you made changes to, add any new files and commit the changes.
$ cd ~/php-docs/doc-en/en $ svn status
Make sure you don't have any files with a
? next to them. Add any that aren't being tracked.
$ svn add path/to/yourfile.xml
Now you can commit your changes. And make your commit message a good one.
$ svn commit -m "Add foo docs for PHP 7"
The first time you commit, it should ask you for your username and password. Just use the one that you now have set up at php.net.
Now you can
cd into the other repo and repeat the same steps.
$ cd ~/php-docs/doc-en/doc-base $ svn status $ svn add foofile.xml $ svn commit -m "Add foo docs for PHP 7"
See your changes online
According to the PHP docs site:
Documentation is built every Friday, then synced out to the website mirrors. However, there is a special mirror at docs.php.net - where the manual is updated from sources every six hours.
And it says, "documentation is built every Friday", it really means, "documentation is built & synced to the mirrors every day". Thanks salathe for that updated info.
But at the maximum, you'll need to wait 6 hours before your wonderful contributions are available on some official PHP servers. And it will eventually make its way to all the mirror servers.
Adding docs for PHP7 and beyond
The first stable PHP 7 release is slated to come out by November 2015. And there's lots you can do to make its debut a solid one. Check out the GoPHP7-ext project to find ways to get involved with PHP 7.
There are a lot of new features of PHP 7, many of which are completely undocumented. If you know of a feature that's undocumented, then you are the one to make that feature documented. If you're not sure what needs to be documented, there's a script for that!
Squashing bugs in the docs
Finding extensions that don't have docs
Belive it or not, there are some functions, classes, methods & ini settings that are part of PHP source that have no documentation at all! Luckily there's a cool script that comes with the
doc-base repo that will scan the docs and flag anything that's missing.
The script can be found in
~/php-docs/doc-en/doc-base/scripts/check-missing-docs.php. It takes an argument
-d which is the sqlite3 database that is generated when you run the PhD script. So in our case we'd run it like so:
$ cd ~/php-docs/doc-en/doc-base/scripts $ php check-missing-docs.php -d ~/php-docs/rendered-docs/index.sqlite
As of June 18th, 2015, this is the output of the script running PHP 7 Alpha 1:
Scanning ini settings Statistics and information: Missing documentation Array ( [functions] => Array (  => get_resources  => preg_replace_callback_array  => deflate_init  => deflate_add  => inflate_init  => inflate_add  => gmp_random_seed  => pcntl_wifcontinued  => pdo_drivers  => error_clear_last ) [methods] => Array (  => BaseException::__construct  => BaseException::getMessage  => BaseException::getCode  => BaseException::getFile  => BaseException::getLine  => BaseException::getTrace  => BaseException::getPrevious  => BaseException::getTraceAsString  => BaseException::__toString  => Generator::getReturn  => DateTimeZone::__wakeup  => DateTimeZone::__set_state  => DateInterval::__wakeup  => DateInterval::__set_state  => DatePeriod::__wakeup  => DatePeriod::__set_state  => DatePeriod::getStartDate  => DatePeriod::getEndDate  => DatePeriod::getDateInterval  => SQLite3::openBlob  => SQLite3::enableExceptions  => SQLite3Stmt::readOnly  => DOMStringList::item  => DOMNameList::getName  => DOMNameList::getNamespaceURI  => DOMImplementationList::item  => DOMImplementationSource::getDomimplementation  => DOMImplementationSource::getDomimplementations  => DOMImplementation::getFeature  => DOMNode::compareDocumentPosition  => DOMNode::isEqualNode  => DOMNode::getFeature  => DOMNode::setUserData  => DOMNode::getUserData  => DOMDocumentFragment::__construct  => DOMDocument::adoptNode  => DOMDocument::renameNode  => DOMNamedNodeMap::setNamedItem  => DOMNamedNodeMap::removeNamedItem  => DOMNamedNodeMap::setNamedItemNS  => DOMNamedNodeMap::removeNamedItemNS  => DOMText::isElementContentWhitespace  => DOMText::replaceWholeText  => DOMUserDataHandler::handle  => DOMErrorHandler::handleError  => DOMConfiguration::setParameter  => DOMConfiguration::getParameter  => DOMConfiguration::canSetParameter  => DOMStringExtend::findOffset16  => DOMStringExtend::findOffset32  => finfo::finfo  => RecursiveRegexIterator::accept  => RecursiveTreeIterator::setPostfix  => SplFileInfo::_bad_state_ex  => SplHeap::isCorrupted  => SplPriorityQueue::getExtractFlags  => SplPriorityQueue::isCorrupted  => mysqli::connect  => mysqli::get_server_info  => mysqli::mysqli  => mysqli::escape_string  => mysqli_result::__construct  => mysqli_result::close  => mysqli_result::free_result  => mysqli_stmt::num_rows  => PDO::__wakeup  => PDO::__sleep  => PDOStatement::__wakeup  => PDOStatement::__sleep  => ReflectionFunctionAbstract::hasReturnType  => ReflectionFunctionAbstract::getReturnType  => ReflectionGenerator::__construct  => ReflectionGenerator::getExecutingLine  => ReflectionGenerator::getExecutingFile  => ReflectionGenerator::getTrace  => ReflectionGenerator::getFunction  => ReflectionGenerator::getThis  => ReflectionGenerator::getExecutingGenerator  => ReflectionParameter::hasType  => ReflectionParameter::getType  => ReflectionType::allowsNull  => ReflectionType::isBuiltin  => ReflectionType::__toString  => ReflectionClass::isAnonymous  => Phar::__destruct  => Phar::getAlias  => Phar::getPath  => PharData::__destruct  => PharData::count  => PharData::getAlias  => PharData::getPath  => PharData::getMetadata  => PharData::getModified  => PharData::getSignature  => PharData::getStub  => PharData::getVersion  => PharData::hasMetadata  => PharData::isBuffering  => PharData::isCompressed  => PharData::isFileFormat  => PharData::offsetExists  => PharData::offsetGet  => PharData::setMetadata  => PharData::setSignatureAlgorithm  => PharData::startBuffering  => PharData::stopBuffering  => PharData::apiVersion  => PharData::canCompress  => PharData::canWrite  => PharData::createDefaultStub  => PharData::getSupportedCompression  => PharData::getSupportedSignatures  => PharData::interceptFileFuncs  => PharData::isValidPharFilename  => PharData::loadPhar  => PharData::mapPhar  => PharData::running  => PharData::mount  => PharData::mungServer  => PharData::unlinkArchive  => PharData::webPhar  => PharFileInfo::__destruct  => PharFileInfo::getContent  => ZipArchive::setCompressionName  => ZipArchive::setCompressionIndex ) [inis] => Array (  => assert.exception  => gd.jpeg_ignore_warning  => highlight.comment  => highlight.default  => highlight.html  => highlight.keyword  => highlight.string  => mail.force_extra_parameters  => mbstring.http_output_conv_mimetypes  => mysqli.rollback_on_cached_plink  => pcre.jit  => report_zend_debug  => session.lazy_write  => sys_temp_dir  => unserialize_callback_func  => user_ini.cache_ttl  => user_ini.filename  => zend.assertions ) [classes] => Array (  => stdClass  => BaseException  => EngineException  => ParseException  => TypeException  => ClosedGeneratorException  => DOMStringList  => DOMNameList  => DOMImplementationList  => DOMImplementationSource  => DOMNameSpaceNode  => DOMTypeinfo  => DOMUserDataHandler  => DOMDomError  => DOMErrorHandler  => DOMLocator  => DOMConfiguration  => DOMStringExtend  => PDORow  => ReflectionGenerator  => ReflectionType  => AssertionException  => XMLWriter ) ) Counts: Missing documentation Array ( [methods] => 125 [classes] => 23 [inis] => 18 [functions] => 10 ) Counts: Documented documentation Array ( [methods] => 1034 [classes] => 116 [inis] => 165 [functions] => 1437 )
According to the comments at the top of the
check-missing-docs.php script, there will be some false-positives on missing docs in the following scenarios:
- The way we document OOP/Procedural together; has id issues
- We don't document all aliases, especially old ones
- Various inconsistencies in the documentation (which this may help find)
- A lot of this has to do with how we document OOP, research this (e.g., inheritance)
- It only checks what you have compiled into your current PHP
So what are you waiting for? Check out the source for some undocumented stuff and get to documenting!
Here are some of the online resources that helped me thought this whole process.
Here are a few other goodies.