Compose project from scratch
I can guess that you are very thrilled with your first PHP scripts or even programs. I think that this is a perfect moment for you to find out a great tool. The tool that is used in many PHP projects. I'm writing about Composer. Composer is a package manager - in other words, it's a tool that will save you a lot of time. I will write about it more someday - but today I will focus only on how you can use it to set up your project.
At this moment I assume that you already visited Composer and you downloaded it. I also assume that you installed it. If no - take a moment and do it. You will thanks me later ;)
composer.json
You may not believe me now - but to start with composer you need only one file that will describe your project. So let create it:
# Create a directory for your project
mkdir myFirstProject
# Now go to it
cd myFirstProject
# And finally create composer.json
vim composer.json
Ok. So below you will find the content of it:
{
"name": "webee-online/php8-base",
"description": "This is a base php project in composer",
"type": "project",
"license": "GPL-3.0-only",
"authors": [
{
"name": "Adam Wojciechowski",
"email": "adam@webee.online"
}
],
"require": {
"php": ">=8.0"
},
"require-dev": {
"kahlan/kahlan": "^5.0",
"phpunit/phpunit": "^9.5",
"symfony/var-dumper": "^5.2",
"friendsofphp/php-cs-fixer": "^2.18"
},
"autoload": {
"psr-4": {
"WeBee\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"WeBee\\Tests\\Spec\\": "spec/",
"WeBee\\Tests\\Unit\\": "tests/"
}
}
}
Basic explanation
And now I will try to explain it a little bit.
- name - it is made of two components vendor and project, I use "webee-online: as my vendor name for all of my projects. It is the name of my company. The second part is an actual name of a project. It should be something simple but meaningful, something that will describe your project - but remember to keep it short and simple;
- description - here you can describe your project in detail - but again keep it short and simple - few sentences or even words.
- type - for now assume you would like to create a project
- license - it is a good idea to define the type of license
- authors - do not be shy and let the world know what you do - I'm more than sure that it is a great thing
Now we start with bear meet:
- require - in this section, you will define all of the dependencies you want to use in your project. E.g when you want to create a command-line utility you can use the Console component from Symfony. To do so you need to add "symfony/console". With each dependency, you must define its version. Usually, you will use the newest one;
- require-dev - it is the same thing as require but for your development purposes. You will use this section for packages for unit testing, code formatting, etc.;
- autoload - in this section, you will define the namespace used for your project (checkout PSR-4 for more details);
- autoload-dev - is the same like autoload but for your development environment;
Ok - as I said before - this is not an article about composer but how to set up a PHP project using it. I will create an article about composer someday - but till then please read about it in composer docs.
Spin it out
Composer will download required dependencies to the vendor folder (do not be worry - it will create it).
# To install necessary dependencies and to generate autoloader files run this command
composer install
Wait a while and you will have your dependencies installed.
Git
You should keep your project in a git repository. But not all files and directories should be tracked by a repository. For example, mentioned before "vendor" directory should be not tracked. Now you will create ".gitignore" file that Git will use to be informed what to skip from the repository.
vim .gitignore
And this is base to start with:
vendor
docs
.phpunit.result.cache
And what does it do:
- line number 1: skip from repository whole vendor directory
- line number 2: skip from repository whole docs directory - I will explain why we need it later
- line number 3: skip from repository file named ".phpunit.result.cache"
That's it. Let's continue with PHPUnit
PHPUnit
PHPUnit is the most popular framework for unit testing. Unit testing is a big topic - but for now, just believe me - you should do it. Now find out how to set up PHPUnit. Check out the bonus at the end of this article :)
You do not need configuration to write or run tests - but it is much easier when you define few things inside the configuration file. So let's do it:
vim phpunit.xml
And this is configuration example:
<?xml version="1.0"?>
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"
colors="true"
bootstrap="vendor/autoload.php"
>
<coverage processUncoveredFiles="true">
<include>
<directory suffix=".php">src</directory>
</include>
<report>
<html outputDirectory="docs/coverage-report" lowUpperBound="50" highLowerBound="100"/>
</report>
</coverage>
<testsuites>
<testsuite name="AllTests">
<directory>test</directory>
</testsuite>
</testsuites>
</phpunit>
What does it means?
In the above config file, we defined where the PHP Unit should look for autoloader (line 7). We defined that we like to generate a code coverage report (a measurement that says how much our code is covered with tests) - lines 9 to 16. To be more precise we like to have an HTML version of this report, that will be saved to docs/coverage-report
directory. And the last thing is in lines 18 to 22 - here we defined the location where we store our tests.
For now, this is all about PHPUnit.
Php-Cs-Fixer
This is a great tool that will help you to keep your code aligned with coding standards. But like any tool you need to configure it.
# Create configuration file for php-cs-fixer
vim php_cs.dist
And config you can start with - this is PHP code:
<?php
return PhpCsFixer\Config::create()
->setUsingCache(false)
->setRiskyAllowed(false)
->setRules([
'@PSR1' => true,
'@PSR2' => true,
'@PSR12' => true,
'@PHP80Migration' => true,
'@Symfony' => true,
'array_syntax' => ['syntax' => 'short'],
])
->setFinder(
PhpCsFixer\Finder::create()
->in(__DIR__)
->append([
__DIR__.'.php_sc.dist'
])
)
;
What does it means?
We asked Php-Cs-Fixer to check our code against rules defined in PSR-1, PSR-2, and PSR-12. Also, we would like to follow Symofny coding standards. We want also our code to be ready for PHP8 migration. Finally, we like to use the short syntax for arrays ([]
instead of array()
).
Php-Cs-Fixer should check all PHP files in the current directory and also should check this configuration file - remember - it is in PHP.
Do some PHP code
You have all the things you need for your first project. But it is hard to check if everything is ok without a code. So I prepared few lines of code for you - this is my bonus :) We will create a Pizza (I hope so you like pizza).
First file we need is Pizza
class located in src/Sample
directory, and it is its content:
<?php
declare(strict_types=1);
namespace WeBee\Sample;
class Pizza
{
private int $size;
public function __construct(int $size = 30)
{
$this->size = $size;
}
public function getSize(): int
{
return $this->size;
}
}
Now we have something to test.
PHP Unit test
First we create PHP Unit tests in src/test/Sample/PizzaTest.php
:
<?php
declare(strict_types=1);
namespace WeBee\Tests\Unit\Sample;
use PHPUnit\Framework\TestCase;
use WeBee\Sample\Pizza;
final class PizzaTest extends TestCase
{
public function testMakingPizza()
{
$this->assertInstanceOf(
Pizza::class,
new Pizza(5),
'Pizza baked successfully'
);
}
public function testMakingPizzaOfWrongSizeType()
{
$this->expectException(\TypeError::class);
new Pizza('30');
}
public function testGettingPizzaSize()
{
$pizza = new Pizza(44);
$this->assertSame(44, $pizza->getSize(), 'Pizza size is correct');
}
}
Kahlan test spec
And the same tests in a different framework called Kahlan. You need to put it into spec/Pizza.spec.php
<?php
declare(strict_types=1);
namespace WeBee\Tests\Spec\Sample;
use WeBee\Sample\Pizza;
describe(
'Pizza',
function () {
it(
'can be baked',
function () {
expect(new Pizza())->toBeAnInstanceOf(Pizza::class);
}
);
it(
'can not be of wrong size type',
function () {
$wrongPizzaSize = function () {
return new Pizza('30');
};
expect($wrongPizzaSize)->toThrow(new \TypeError());
}
);
it(
'is of correct size',
function () {
$pizza = new Pizza(44);
expect($pizza->getSize())->toBeAn('integer');
expect($pizza->getSize())->toBe(44);
}
);
}
);
Find a moment and compare both ways of testing. Check which one is more readable for you. I prefer Kahlan - but this is the topic for another article.
How to use it
# To fix coding standards:
./vendor/bin/php-cs-fixer fix
# To just check what is wrong:
./vendor/bin/php-cs-fixer --dry-run --diff fix
# To execute tests in PHP Unit:
./vendor/bin/phpunit
# To do the same thing in Kahlan:
./vendor/bin/kahlan
I think that it is more than enough for one article. You can also download all the code from my repository PHP 8 Project Template.
Stay tuned and write your code!