Skip to content

rougin/basilisk

Repository files navigation

Basilisk

Latest Version on Packagist Software License Build Status Coverage Status Total Downloads

Basilisk is a project skeleton specifically for the Slytherin PHP micro-framework which contains an opinionated code structure based on my experiences creating projects using Slytherin as its foundation. The code structure should be easy to understand and be under SOLID principles.

Installation

Create a new Basilisk project via Composer:

$ composer create-project rougin/basilisk "hogwarts"

Once created, kindly execute the one-time setup to the project:

$ php setup.php

Running the project

To run Basilisk in a web browser, the PHP's built-in web server can be used:

$ php -S localhost:80 -t app/public

After running, open a web browser then proceed to http://localhost in a new tab.

Warning

This command should only be used for development purposes. It is recommended to use Apache or Nginx in running this project.

What's inside?

A Basilisk project contains a configuration of the following packages:

Slytherin

Slytherin is a simple and extensible PHP micro-framework that tries to achieve a SOLID-based design for creating web applications. As Slytherin is the core foundation of Basilisk, Basilisk has also configured the following packages of Slytherin out of the box:

Note

Kindly see the Slytherin's documentation on how to use the above-mentioned packages.

Dotenv

Dotenv is a simple PHP package that loads environment variables from .env files to getenv(), $_ENV and $_SERVER automagically. This is one of the core packages of Basilisk as it uses environment variables in most of its configuration in the app/config directory:

# .env.example

#######################
# Application Settings
#######################
APP_NAME="Basilisk"
APP_VERSION="0.1.0"

# ...
// app/config/app.php

return array(

    // ...

    'name' => getenv('APP_NAME'), // returns "Basilisk"

    // ...

    'version' => getenv('APP_VERSION'), // returns "0.1.0"

    // ...

);

Phinx

Phinx is a tool made for PHP in performing database migrations. Being a framework-agnostic package, Phinx can create, write and perform database migrations easily. To use this package, kindly install it first using Composer:

$ composer require robmorgan/phinx

Once installed, kindly check the app/config/phinx.php file on the variables that requires an update:

// app/config/phinx.php

return array(

    /**
     * Paths to be used for database migration.
     *
     * @var array<string, string[]>
     */
    'paths' => array(

        /**
         * @var array
         */
        'migrations' => array(
            $root . '/src/Phinx/Scripts',
        ),

        /**
         * @var array
         */
        'seeds' => array(
            $root . '/src/Phinx/Seeders',
        ),

    ),

    // ...

);

When creating or performing database migrations using Phinx, always use the configuration file provided by Basilisk:

$ vendor/bin/phinx create CreateUsersTable -c app/config/phinx.php

Weasley

Weasley is a utility package in PHP that provides generators, helpers, and utilities for Slytherin. The following packages are also configured within Basilisk:

  • Laravel\Eloquent enables the usage of Eloquent to Basilisk which is an Object-relational mapper (ORM) from Laravel. To use this package, kindly install its required package first in Composer:

    $ composer require illuminate/database
  • Laravel\Blade allows Basilisk to use Blade from Laravel for creating PHP templates using the Blade templating engine. To use this package, kindly uncomment its related code first in the app/config/app.php file:

    // app/config/app.php
    
    // ...
    
    /**
     * This section specifies the core integrations that are
     * required to run in Slytherin. The following will use
     * the defined HTTP variables, available HTTP routes, and
     * the specified HTTP middlewares (if they're available).
     */
    'Rougin\Slytherin\Http\HttpIntegration',
    'Rougin\Slytherin\Integration\ConfigurationIntegration',
    'Rougin\Slytherin\Middleware\MiddlewareIntegration',
    'Rougin\Slytherin\Routing\RoutingIntegration',
    // 'Rougin\Slytherin\Template\RendererIntegration', // comment this line
    
    /**
     * This section specifies the packages came from Weasley.
     * Please see Weasley's documentation for all of its
     * available packages and integrations that can be used.
     *
     * @link https://roug.in/weasley/
     */
    'Rougin\Weasley\Packages\Laravel\Eloquent',
    'Rougin\Weasley\Packages\Laravel\Blade', // uncomment this line

    Then proceed to install its required package from Composer:

    $ composer require illuminate/view

    Once installed, kindly the Blade templates in the app/blades directory:

    // app/blades/index.blade.php
    
    @extends('main')
    
    @section('content')
      <div class="container">
        <div class="p-5">
          <h1 class="text-center">Hello, Muggle!</h1>
        </div>
      </div>
    @endsection

Directory structure

The following directory names below are only the preferred names based on my experience building projects using Slytherin. However, they can be easily be extended or removed as Slytherin not does not conform to any of the specified preferences:

src/
├─ Checks/
├─ Depots/
├─ Models/
├─ Phinx/
│  ├─ Scripts/
│  ├─ Seeders/
├─ Routes/
├─ Scripts/

Checks

This directory contains PHP classes that are used specifically for validation. The said PHP classes may be extended to the Check class of Weasley:

namespace App\Checks;

use Rougin\Weasley\Check;

class UserCheck extends Check
{
    /**
     * @var array<string, string>
     */
    protected $labels =
    [
        'name' => 'Name',
        'email' => 'Email',
    ];

    /**
     * @var array<string, string>
     */
    protected $rules =
    [
        'name' => 'required',
        'email' => 'required|email',
    ];
}

Note

For more information on how to create a validation class, please see the Validation section of the Weasley documentation.

Depots

This is the main directory that should contain the business logic of Basilisk:

namespace App\Depots;

use App\Models\User;

class UserDepot
{
    /**
     * @var \App\Models\User
     */
    protected $user;

    /**
     * @param \App\Models\User $user
     */
    public function __construct(User $user)
    {
        $this->user = $user;
    }

    /**
     * @return array<string, mixed>[]
     */
    public function all()
    {
        $result = $this->user->all();

        $items = array();

        // ...

        return $items;
    }
}

Prior in using depots, I implemented most of the logic in the Routes or Models directories. However, it presents a challenge to me in organizing code when implementing new features. In using depots, I can reuse the same logic in to either Routes (for receiving user request) or in Scripts directory (for handling terminal-based actions).

Note

In other PHP frameworks and other guides, Depot is also known as the Repository pattern.

Models

This is the directory where for storing the models (if using Eloquent) or entities (if using Doctrine). In my experience, my best practice is that the class names added in this directory should represent a database table (e.g., if having a users table from the database, it should be represented in Basilisk as User class):

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * @var string[]
     */
    protected $fillable =
    [
        'name',
        'password',
        'email',
    ];

    /**
     * @var string[]
     */
    protected $hidden =
    [
        'password'
    ];

    /**
     * @var string
     */
    protected $table = 'users';
}

Note

As Basilisk provided a User model based on Eloquent, kindly see its official discussion for its usage and configuration.

Phinx

This is the directory for the storage of related files to the Phinx package. The Scripts directory must contain the generated database migrations while the Seeders directory must contain the custom database seeders:

// src/Phinx/Scripts/20171012020230_create_users_table.php

use Phinx\Migration\AbstractMigration;

class CreateUsersTable extends AbstractMigration
{
    public function change(): void
    {
        $properties = ['id' => false, 'primary_key' => ['id']];

        $table = $this->table('users', $properties);

        $table->addColumn('id', 'integer', ['limit' => 10, 'identity' => true]);
        $table->addColumn('name', 'string', ['limit' => 200]);
        $table->addColumn('email', 'string', ['limit' => 200]);
        $table->addColumn('password', 'string', ['limit' => 500]);
        $table->addColumn('created_at', 'datetime');
        $table->addColumn('updated_at', 'datetime', ['null' => true]);

        $table->create();
    }
}
// src/Phinx/Seeders/UserSeeder.php

use Phinx\Seed\AbstractSeed;

class UserSeeder extends AbstractSeed
{
    protected $items =
    [
        ['name' => 'Harry Jonathans Potter', 'email' => '[email protected]'],
        ['name' => 'Hermione Jane Granger', 'email' => '[email protected]'],
        ['name' => 'Ronald Bilius Weasley', 'email' => '[email protected]'],
    ];

    public function run(): void
    {
        $data = array();

        foreach ($this->items as $item)
        {
            $item['created_at'] = date('Y-m-d H:i:s');

            $data[] = $item;
        }

        $this->table('users')->insert($data)->save();
    }
}

This directory will also be used for performing database migrations and its seeders. To perform a database migration, kindly run the migrate command:

$ vendor/bin/phinx migrate -c app/config/phinx.php

The seed:run command can be used for populating data in a database:

$ vendor/bin/phinx seed:run -c app/config/phinx.php

When executed, the command above will perform the database seeders in alphabetical order.

Note

Before performing any database migrations from Phinx, kindly update the database credentials first in .env:

$ cp .env.example .env

Routes

This is the gateway of Basilisk wherein the HTTP routes are stored and configured as a PHP class. The PHP class can call or instantiate the classes found from the previously mentioned directories:

namespace App\Routes;

use Rougin\Slytherin\Template\RendererInterface;

class Hello
{
    /**
     * Returns the "Hello, Muggle!" text.
     *
     * @return string
     */
    public function index(RendererInterface $renderer)
    {
        return $renderer->render('index');
    }
}

Once a HTTP route was created to the directory, it should be added to the Router for it to be recognized by Slytherin:

namespace App;

use Rougin\Slytherin\Routing\Router as Slytherin;

class Router extends Slytherin
{
    /**
     * @var string
     */
    protected $namespace = 'App\Routes';

    /**
     * @var string
     */
    protected $prefix = '/';

    /**
     * @return \Rougin\Slytherin\Routing\RouteInterface[]
     */
    public function routes()
    {
        $this->get('/', 'Hello@index');

        // ...

        return $this->routes;
    }
}

Note

In other PHP frameworks, Routes is commonly known as Controllers.

Scripts

This is the directory to store commands or scripts. These scripts can be executed directly using the php command in the terminal:

// src/Scripts/HelloWorld.php

echo 'Hello world!';
$ php src/Scripts/HelloWorld.php

Hello world!

Development workflow

Basilisk also provides unit testing and static code analysis out of the box when implementing new features to minimize the introduction of errors during development. It also has an opinionated coding style which can be configured after the installation.

Unit testing

The sample unit tests provided in Basilisk were written in PHPUnit:

$ composer test

It is recommended to run the above command to always check if the updated code introduces errors when creating fixes or implementing new features.

Note

Please see the official documentation of PHPUnit on how to write unit tests to the specified testing framework.

Code quality

To retain the code quality of Basilisk, a static code analysis code tool named PHPStan can be used during development. To start, kindly install the specified package in the global environment of Composer:

$ composer global require phpstan/phpstan --dev

Once installed, PHPStan can now be run using its namesake command:

$ phpstan

Note

When running phpstan, it will use the phpstan.neon file which is already provided by Basilisk.

Coding style

Aside from code quality, Basilisk also uses a tool named PHP Coding Standards Fixer for maintaining an opinionated style guide. To use this tooling, it needs also to be installed in the Composer's global environment first:

$ composer global require friendsofphp/php-cs-fixer --dev

After its installation, kindly use the php-cs-fixer command in the same Authsum directory:

$ php-cs-fixer fix --config=phpstyle.php

The phpstyle.php file provided by Basilisk currently follows the PSR-12 standard as its baseline for the coding style and uses Allman as its indentation style.

Note

Installing both PHPStan and PHP Coding Standards Fixer requires a minimum version of PHP at least 7.4.

Changelog

Please see CHANGELOG for more information what has changed recently.

License

The MIT License (MIT). Please see LICENSE for more information.