This TYPO3 extension, based on Extbase and Fluid, is an example of best
practices in building an extension and securing its quality by automated code
checks, unit/functional/acceptance testing and continuous integration (CI).
This TYPO3 extension, based on Extbase and Fluid, is an example of best
practices in building an extension and securing its quality by automated code
checks, unit/functional/acceptance testing and continuous integration (CI).
Note
This is not a kickstarter extension.
This extension should not be used to kickstart other extensions.
Instead, this extension should serve as an example for best practices.
Why is this extension called "tea"?
This extension aims to cover all fundamental aspects of developing an Extbase
extension. The fictional use case is the management of tea varieties. The
required models, classes, controllers, and other components represent all
typical parts of an Extbase extension. Additional examples demonstrate how to
test these components.
Since well-chosen extension names should reflect their domain, this extension is
named “tea”. While we could have added _example to indicate that it is a
sample extension, this would contradict our goal of demonstrating best practices.
The target group for this extension is TYPO3 extension developers. Typical use
cases include:
Gaining inspiration by exploring the code
Cloning the tea extension locally to access a working example of tests, which can then be adapted for your own extension
Presentation at the TYPO3 Online Days 2021
At the TYPO3 Online Days 2021, Oliver Klee
held a session presenting our approach to automate extension code quality.
Have a look at the slides and the video of the presentation!
Slides
Video
Development environment
You can run the code quality checks and automated tests locally (using a
local PHP, Composer, and database) or using runTests.sh.
To kickstart the project, we suggest the usage of the
TYPO3-testing-distribution
by Oliver Klee as development environment. The distribution comes with a frontend,
example data and predefined plugins.
The following commands expect that your SSH key is known by GitHub. If not, you need to use https://.
You can organize the folder structure as you wish, but lets say your folder
structure looks like this:
git\
TYPO3-testing-distribution
tea
Copied!
Inside the testing distribution there is a file
docker-compose.extensions.yaml.template which mounts the used extensions. This file need to be renamed and adjusted.
For the TYPO3 versions we currently support, this extension will support all
PHP versions officially supported by those TYPO3 versions.
In addition, we are doing our best to support newer PHP versions as soon as
they are available (even release candidates, alphas and betas).
Before we mark a PHP version as supported via composer.json and
ext_emconf.php, we ensure that all tests pass on that PHP
version. (That also means that if you want to install the extension on that
PHP version nonetheless, you will need to tweak Composer's platform settings
to bypass this.)
Release and Branching Strategy
When maintaining TYPO3 extensions, developers often need to support multiple
TYPO3 Long-Term Support (LTS) versions simultaneously. This typically requires
a clear release and branching strategy to manage development and maintenance
across different TYPO3 versions.
There are two common strategies for managing branches when supporting multiple
TYPO3 LTS versions.
Approach 1: One branch for multiple TYPO3 LTS versions
In this approach, there is a single main branch that receives new features and
updates, while supporting multiple TYPO3 LTS versions at the same time.
The downside of this approach is that it may require some version-dependent
code switches, which can increase complexity. However, the major advantage is
that there is only one branch to maintain, making it easier to implement new
features and code changes across all supported TYPO3 versions.
This approach simplifies the maintenance of the extension and is preferred when
minimizing maintenance overhead is the primary concern.
Approach 2: Separate branch per TYPO3 LTS version
In this approach, there is one main branch for each TYPO3 LTS version. This
means that each branch exclusively supports a single TYPO3 LTS version.
The advantage here is that version-specific code can be used without requiring
version-dependent switches, reducing complexity in the codebase. However, this
approach increases the maintenance burden, as any new features or updates must
be applied to each branch individually.
This approach may be necessary when there are significant differences between
TYPO3 LTS versions or when you want to avoid version-dependent code.
Conclusion
The appropriate release and branching strategy depends on the specific
requirements of the extension and the TYPO3 versions you are supporting. If
reducing maintenance complexity is a priority, using a single branch for
multiple versions is often the better choice. However, if you need to tailor
your extension for each version, a separate branch for each version may be more
suitable.
Dependency manager
To keep things simple, most development tools, for example php-cs-fixer, are
installed by Composer as development requirements and in some cases where
installation via Composer is not possible, we use PHIVE.
PHIVE packages each tool with all its dependencies as
a separate PHAR. This helps avoid dependency hell (which means that you cannot
install or upgrade some tool as the tool's dependencies conflict with the
dependencies on another library). It also allows running versions of tools
that require a PHP version that is higher than the lowest allowed PHP version
for this project.
Using PHIVE to install phpunit/phpcov
To support php version 7.4 and 8.2 in the tea extension, we are using PHIVE
to install phpunit/phpcov.
We need phpunit/phpcov in version 10 to support php 8.2.
Our minimum php version 7.4 would prevent the installation with composer.
Note
To find more information about install and usage,
please check out the documentation of PHIVE.
Command Controller
The "tea" extension comes with a CommandController that can be used for the
automatic creation of test data. It also serves to illustrate how data can be
created in the database using a command controller.
You must set a page id as argument. Therefore it's necessary to create an
sysfolder before.
You can add option -d to delete already existing data.
For most development-related tasks, this extension provides Composer scripts.
If you are working locally (Composer needs to be installed on your local
machine), you can run them using
composer <scriptname>.
You can run
composer or
./Build/Scripts/runTests.sh -s composer to
display a list of all available Composer commands and scripts. For all custom
Composer scripts there are descriptions in the script-description section of
the composer.json.
Keep in mind that certain commands only work if you have run
composer install or
./Build/Scripts/runTests.sh -s composer install
before.
If you have problems with missing dependencies on your local machine, it is
recommended to execute tests with the usage of the runTests.sh script.
This makes your life easier because you
don't have to worry about local dependencies.
Note
It's always a good idea to look into the runTests.sh "Examples" section to
get an idea how it works. In the "Options" section you can find all
available options.
Running code checks
You can currently run these code checks on the command line:
Before running any commands you need to install all dependencies with
./Build/Scripts/runTests.sh -s composer update
Copied!
You can currently run these tests on the command line:
./Build/Scripts/runTests.sh -s functional
Copied!
Runs the functional tests using a database populated from the CSV files in
Tests/Functional/Controller/Fixtures/Database folder.
Note
For executing functional tests, a database connection is needed. Therefore,
it is recommended to run the functional tests using runTests.sh.
./Build/Scripts/runTests.sh -s unit
Copied!
Runs the unit tests.
Tip
With the option -p, you can specify which PHP version will be used to
execute the tests. If you don't specify a PHP version, the default PHP
version will be used.
./Build/Scripts/runTests.sh -p 8.2 -s unit
Copied!
Running unit and functional tests in PHPStorm
General setup
Open File > Settings > PHP > Test Frameworks.
(*) Use Composer autoloader.
Path to script: select .Build/vendor/autoload.php in your project folder.
In the Run configurations, edit the PHPUnit configuration and use these
settings so this configuration can serve as a template:
Directory: use the Tests/Unit directory in your project.
(*) Use alternative configuration file.
Use .Build/vendor/typo3/testing-framework/Resources/Core/Build/UnitTests.xml
in your project folder.
Add the following environment variables:
typo3DatabaseUsername
typo3DatabasePassword
typo3DatabaseHost
typo3DatabaseName
Unit tests configuration
In the Run configurations, copy the PHPUnit configuration and use these
settings:
Directory: use the Tests/Unit directory in your project
Functional tests configuration
In the Run configurations, copy the PHPUnit configuration and use these
settings:
Directory: use the Tests/Functional directory in your project.
(*) Use alternative configuration file.
Use
.Build/vendor/typo3/testing-framework/Resources/Core/Build/FunctionalTests.xml.
Continuous integration
As an example, this extension provides several ways to perform the code quality
checks and tests in a CI pipeline. You can copy the
appropriate configuration depending on which Git hosting service and CI
platform you want to use.
This extension has a code-checking workflow for
GitHub Actions using
local tools:
This workflow uses the development tools installed via Composer and PHIVE and
calls them using the provided Composer scripts. This allows running the code
quality checks locally as well as in GitHub Actions.
Documentation rendering
In order to render the documentation, please use the runTests.sh wrapper:
./Build/Scripts/runTests.sh -s docsGenerate
Copied!
You do not need to start or build the containers for this as this happens
automatically.
Libraries and extensions do not need the security check as they should not have
any restrictions concerning the other libraries they are installed alongside
with (unless those would create breakage). Libraries and extension also should
not have a version-controlled composer.lock (which usually is used for
security checks).
Instead, the projects and distributions (i.e., for TYPO3 installations) need to
have the security checks.
Our approach to assertions and type checks
In general, we use assertions and type checks to catch bad input, to help debug
problems, and to help static type analysis tools like PHPStan or Psalm to
determine the type and range of a variable where it cannot detect it
automatically.
In production code
To catch bad input that actually is possible, we throw an exception. This allows
developers and users to see what is wrong and to fix it.
if (!$model instanceof Tea) {
thrownew \RuntimeException('No model found.', 1687363745);
}
Copied!
To help PHPStan and Psalm with variables where we (as developers) know the type
and range for sure, but the tool cannot determine it automatically, we use
assertions.
This makes the type and range obvious both to PHPStan as well as the human
reader.
TYPO3 also extends the availability of registering services via attributes.
We will prefer the attributes over Services files.
But we won't use both at once and still need Services files as long as PHP 7.4 is supported.
Attributes will be used in favor of Services files once we drop unsupported PHP versions.
Services Files
We choose to use Services.php instead of Services.yaml.
It still is completely fine to use YAML files over PHP files or even mix both.
Some things are way shorter to write with the YAML syntax.
We prefer the PHP file over YAML for the following reasons:
Static Code Analysis
Static code analysis tools, like PHPStan, can analyse the PHP source code base.
They typically don't support other files like YAML.
Those tools report issues for not found classes, e.g. due to typos.
Auto completion
Modern tooling like IDEs and Language Servers provide auto completion for PHP source files out of the box.
That way programmers can discover APIs and write more robust code faster.
Automatic code migration
PHP Code can be auto migrated via tools like rector.
E.g. renaming a class can be applied to PHP code, but no current tool for yaml exists.
Sitemap
Reference to the headline
Copy and freely share the link
This link target has no permanent anchor assigned.The link below can be used, but is prone to change if the page gets moved.