Timber Logo

You are reading the documentation for Timber v2.x. Switch to the documentation for Timber v1.x.

Contributing to Timber

Hey there! We’re really happy you’ve found your way here 💪. Timber is a community project that is developed by people from all over the world. We appreciate any help.

Here are ways to get involved:

  1. Star the project!
  2. Answer questions that come in through GitHub issues.
  3. Report a bug that you find.
  4. Share a theme you’ve built with Timber. This helps transfer knowledge about best practices, etc. Add it to the Showcase list.
  5. Tweet and blog about the advantages (and criticisms) of the project and Twig.
  6. Browse "help wanted" and "good first issue" labels for areas of WordPress/PHP/code you know well to consider, build or document.
  7. Answer questions on Stack Overflow posted under the «Timber» tag. You can also subscribe to a tag via email to get notified when someone needs help.
  8. Answer questions and join in on GitHub Discussions.

Pull Requests #

Pull requests are highly appreciated. More than 150 people have written parts of Timber (so far). Here are some guidelines to help:

  1. Solve a problem – Features are great, but even better is cleaning-up and fixing issues in the code that you discover.
  2. Write tests – This helps preserve functionality as the codebase grows and demonstrates how your change affects the code.
  3. Write documentation – Timber is only useful if its features are documented. This covers inline documentation of the code as well as documenting functionality and use cases in the Guides section of the documentation.
  4. Small > big – Better to have a few small pull requests that address specific parts of the code, than one big pull request that jumps all over.
  5. Comply with Coding Standards – See next section.

Preparations #

After you’ve forked the Timber repository, you should install all Composer dependencies.

composer install

Coding Standards #

We use EasyCodingStandard for Timber’s code and code examples in the documentation, which follows the PSR-12: Extended Coding Styles.

We use tools to automatically check and apply the coding standards to our codebase (including the documentation), reducing the manual work to a minimum.

To run all checks, you can run the qa script.

composer qa

Check and apply coding standards #

We use EasyCodingStandard to automatically check and apply the coding standards.

composer cs

You can also apply coding style fixes automatically.

composer cs:fix

Check and apply coding standards to the documentation #

We can not only check coding standards in the code, but also in the Markdown documentation.

composer cs:docs

To apply the fixes automatically to the documentation, run:


Enforcing coding standards using pre-commit hooks #

We use GrumPHP to add pre-commit hooks which automatically check the committed code changes for any coding standard violations.

You can fix these issues automatically by running composer ecs:fix.

Other commands #

There are more commands that we use to ensure code quality. Usually, you won’t need them.

  • composer analyze – Runs static analysis using PHPStan.
  • composer lint-composer – Checks for inconsistencies in composer.json.
  • composer lint-composer:fix – Ensures composer.json consistency.

Hooks #

We use hook names namespaced by / instead of underscores.

// 🚫
$context = apply_filters( 'timber_context', $context );

// ✅
$context = apply_filters( 'timber/context', $context );

Inline Documentation #

The Reference section of the documentation is automatically generated from the inline documentation of the Timber code base. To document Timber, we follow the official PHP Documentation Standards of WordPress.

There are minor differences to the official standards:

  • For class headers, we don’t use @package or @subpackage tags.
  • We don’t use the @access tag.

Ignoring Structural Elements #

The documentation generator ignores a number of elements. An element (class, method, property) is ignored when one of the following conditions applies:

  • No DocBlock is provided
  • No @api tag is present
  • An @ignore tag is present
  • An @internal tag is present
  • The visibility is private (applies to methods only)

This means that for Markdown files to be generated for a class at all, you’ll need at least a DocBlock with an @api tag.

Referencing class names #

When referencing a namespaced class name in a type (for example in a @param or @return tag), then use the fully qualified name. Example: \Timber\Post instead of just Post.

Code examples #

Timber uses tabs for indentation, but you should always use spaces for indentation in code examples, because the resulting Markdown will have a more consistens styling.

The @example tag allows you add code examples to your DocBlocks, including fenced code blocks:

* Function summary.
* Function description.
* @api
* @example
* Optional text to describe the example.
* ```php
* my_method( 'example', false );
* ```
* @param string $param1 Description. Default 'value'.
* @param bool $param2 Optional. Description. Default true.

function my_method( $param1, $param2 = true ) {}

Reference linking with @see tag #

When you use the @see tag, the Reference Generator will automatically convert it to a link to the reference.

  • Use this tag only when the referenced method has an @api tag, which means that it is public.
  • Beware, you’ll always use the notation with ::, which you normally know from static methods. But even if the method that you link is not static, you’ll have to use the double colon.

An example:

@see \Timber\Image::src()

will turn into this:

<a href="/docs/reference/timber-image/#src">Timber\Image::src()</a>

Documenting Hooks #

Follow the official Inline Documentation Standards for Actions and Filters to document hooks.

Keywords for filters and actions #

A DocBlock that documents a hook should always start with the keyword

  • Filters for filter hooks
  • Fires for action hooks

This is needed so that the Hook Reference Generator can detect the comments associated with filters and actions.

Syntax #

* Filters … / Fires … (Summary)
* Description.
* `$var` Optional description of variables that might be used in the filter name.
* @see \Timber\Classname::function()
* @link https://github.com/timber/timber/pull/1254
* @since x.x.x
* @deprecated x.x.x
* @example
* Optional text to describe the example.
* ```php
* // A PHP example
* /**
* * Multiline comments are possible as well.
* * You’ll need to escape the closing tag with a "\".
* *\/
* ```
* ```twig
* {# A Twig example #}
* ```
* @param type $var Description. Default 'value'.
* @param array $args {
* Short description about this hash.
* @type type $var Optional. Description. Default value.
* @type type $var Optional. Description. Default value.
* }
* @param type $var Optional. Description. Default value.

Dynamic filters #

When a filter contains a variable, it should be marked up with double quotes " and the variable name inside curly braces:

$force = apply_filters( "timber/transient/force_transient_{$slug}", $force );

Additionally to this, document what the variable is by adding it to the description. Add it on a new line with the variable wrapped in backticks, so that it appears as code in Markdown:

* Filters the status of a particularly named transient.
* Allows you to override the behavior on a case-by-case basis.
* `$slug` The transient slug.
* @param bool $force Param description.

$force = apply_filters( "timber/transient/force_transient_{$slug}", $force );

Multiline declaration #

Formatting a filter into multiple lines when the line should be too long is allowed:

* Filters …

$force = apply_filters_deprecated(
array( $force ),

Unfinished filters #

If a filter description is not finished yet, mark it up with the @todo tag. It’s okay if you don’t know what a filter is doing exactly or if you’re unsure about what a parameter does. Describe what needs to be done in the @todo tag.

* Filters …
* @todo Add summary, add description.
* @param bool $force Param description.

As soon as the todo is resolved, the @todo tag can be removed.

Unit tests #

Install WordPress test suite #

Run the following command to install the test suite on your local environment:

bash bin/install-wp-tests.sh {db_name} {db_user} {db_password} {db_host} {wp_version}

Replace variables with appropriate values.

Run unit tests #

Run PHPUnit test suite with the default settings and ensure your code does not break existing features.

composer test

You can also run the tests without coverage.

composer test:no-cov

Process #

All PRs receive a review from at least one maintainer. We’ll do our best to do that review as soon as possible, but we’d rather go slow and get it right than merge in code with issues that just lead to trouble.

GitHub reviews & assignments #

You might see us assign multiple reviewers, in this case these are OR checks (i.e. either Jared or Nicolas) unless we explicitly say it’s an AND type thing (i.e. can both Lukas and Maciej check this out?).

We use the assignee to show who’s responsible at that moment. We’ll assign back to the submitter if we need additional info/code/response, or it might be assigned to a branch maintainer if it needs more thought/revision (perhaps it’s directly related to an issue that's actively being worked on).

Once approved, the lead maintainer for the branch should merge the PR into the master or 2.x branch. The 1.x team will work to resolve merge conflicts on #1617 (2.x into master) so the branches stay in sync.

Codeowners #

We use a CODEOWNERS file to define who gets automatic review invitations.