Skip to content

iviphp/ivi

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

145 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Ivi

The official application skeleton for building modular web and console applications with IviPHP.

iviphp/ivi provides a minimal project structure powered by iviphp/framework. It includes application bootstrapping, configuration, service providers, console commands and writable storage directories without imposing a large application architecture.

Requirements

  • PHP 8.2 or later
  • Composer
  • Required PHP extensions for the database driver used by the application

Installation

Create a new IviPHP application:

composer create-project iviphp/ivi my-application

Enter the project directory:

cd my-application

Create the local environment file:

cp .env.example .env

Make the console executable:

chmod +x bin/console

Display the available commands:

php bin/console

or:

./bin/console

Project structure

.
├── bin/
│   └── console
├── bootstrap/
│   └── app.php
├── config/
│   ├── app.php
│   └── database.php
├── public/
│   └── index.php
├── src/
│   ├── Console/
│   │   └── Commands/
│   │       └── AboutCommand.php
│   ├── Http/
│   │   └── Controllers/
│   └── Providers/
│       └── AppServiceProvider.php
├── storage/
│   ├── cache/
│   ├── logs/
│   └── views/
├── .env.example
├── composer.json
├── LICENSE
└── README.md

Application bootstrap

The application is created in:

bootstrap/app.php

This file:

  • loads Composer;
  • reads the local .env file;
  • loads application configuration;
  • creates the framework instance;
  • registers application service providers;
  • registers console commands;
  • returns the configured framework.

Example:

<?php

declare(strict_types=1);

use App\Console\Commands\AboutCommand;
use App\Providers\AppServiceProvider;
use Ivi\Config\Config;
use Ivi\Framework\Framework;

$basePath = dirname(__DIR__);

require_once $basePath
    . '/vendor/autoload.php';

$config = Config::load(
    $basePath . '/config'
);

$framework = Framework::create(
    basePath: $basePath,
    config: $config,
    environment: (string) $config->get(
        'app.environment',
        'production'
    ),
    consoleName: (string) $config->get(
        'app.name',
        'Ivi Application'
    ),
    consoleVersion: (string) $config->get(
        'app.version',
        '0.1.0'
    )
);

$framework->provider(
    AppServiceProvider::class
);

$framework->registerCommand(
    new AboutCommand()
);

return $framework;

The bootstrap file may be reused by console and web entry points.

Environment configuration

Create .env from the example file:

cp .env.example .env

Default values:

APP_NAME="Ivi Application"
APP_ENV=development
APP_DEBUG=true
APP_URL=http://localhost:8000

DB_DRIVER=sqlite
DB_DATABASE=storage/database.sqlite
DB_HOST=127.0.0.1
DB_PORT=3306
DB_NAME=ivi
DB_USERNAME=root
DB_PASSWORD=

CACHE_DRIVER=file
CACHE_PATH=storage/cache

LOG_CHANNEL=file
LOG_LEVEL=debug
LOG_PATH=storage/logs/app.log

The .env file is ignored by Git and should contain machine-specific or private configuration.

Do not commit production credentials.

Application configuration

Application configuration is stored in:

config/app.php

Available settings include:

return [
    'name' => 'Ivi Application',
    'version' => '0.1.0',
    'environment' => 'production',
    'debug' => false,
    'url' => 'http://localhost:8000',
    'timezone' => 'UTC',
    'locale' => 'en',
];

Retrieve configuration through the framework:

$name = $framework
    ->config()
    ->get(
        'app.name',
        'Ivi Application'
    );

Check the current environment:

if ($framework->isEnvironment('production')) {
    // Production behavior.
}

Check several environments:

if (
    $framework->isEnvironment(
        'development',
        'testing'
    )
) {
    // Development or testing behavior.
}

Database configuration

Database configuration is stored in:

config/database.php

The default driver is SQLite:

DB_DRIVER=sqlite
DB_DATABASE=storage/database.sqlite

Create the SQLite database file:

touch storage/database.sqlite

MySQL

DB_DRIVER=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_NAME=ivi
DB_USERNAME=root
DB_PASSWORD=

PostgreSQL

DB_DRIVER=pgsql
DB_HOST=127.0.0.1
DB_PORT=5432
DB_NAME=ivi
DB_USERNAME=postgres
DB_PASSWORD=

The selected database package or provider is responsible for consuming this configuration and establishing connections.

Service providers

Application services are registered through service providers.

The default provider is:

src/Providers/AppServiceProvider.php

It registers application metadata and common paths in the service container.

Available services include:

app.name
app.version
app.environment
app.debug
app.url

path.base
path.bootstrap
path.config
path.public
path.storage
path.cache
path.logs
path.views

Resolve a service:

$name = $framework->make('app.name');

$storagePath = $framework->make(
    'path.storage'
);

Check whether a service exists:

if ($framework->has('path.logs')) {
    $logs = $framework->make(
        'path.logs'
    );
}

Creating a service provider

Create a provider in:

src/Providers

Example:

<?php

declare(strict_types=1);

namespace App\Providers;

use App\Services\Mailer;
use Ivi\Framework\Contracts\ApplicationInterface;
use Ivi\Framework\Providers\ServiceProvider;

final class MailServiceProvider extends ServiceProvider
{
    public function __construct(
        ApplicationInterface $application
    ) {
        parent::__construct(
            $application,
            [
                Mailer::class,
                'mailer',
            ]
        );
    }

    protected function registerServices(): void
    {
        $mailer = new Mailer();

        $this->container()->instance(
            Mailer::class,
            $mailer
        );

        $this->container()->instance(
            'mailer',
            $mailer
        );
    }

    protected function bootServices(): void
    {
        $mailer = $this->make(
            Mailer::class
        );

        $mailer->initialize();
    }
}

Register it in bootstrap/app.php:

use App\Providers\MailServiceProvider;

$framework->provider(
    MailServiceProvider::class
);

Register several providers:

$framework->providers([
    App\Providers\DatabaseServiceProvider::class,
    App\Providers\MailServiceProvider::class,
]);

Provider lifecycle

Service providers have two lifecycle stages.

Registration

protected function registerServices(): void
{
    // Register container services.
}

Registration happens when the provider is added to the framework.

Use this stage for:

  • service instances;
  • factories;
  • aliases;
  • configuration defaults;
  • dependency bindings.

Booting

protected function bootServices(): void
{
    // Complete application initialization.
}

Booting happens after application bootstrap completes.

Use this stage for:

  • resolving services;
  • initializing components;
  • registering listeners;
  • preparing directories;
  • connecting framework modules.

Console commands

Console commands are stored in:

src/Console/Commands

The default application contains:

AboutCommand

Run it with:

php bin/console about

or through its alias:

php bin/console info

Example output:

IviPHP
A modular PHP framework for web and console applications.

Application:
  Name: Ivi Application
  Version: 0.1.0
  Environment: development

Runtime:
  PHP: 8.2.x
  SAPI: cli
  Operating system: Linux

Creating a command

Create a command class:

<?php

declare(strict_types=1);

namespace App\Console\Commands;

use Ivi\Console\Contracts\CommandInterface;
use Ivi\Console\Contracts\InputInterface;
use Ivi\Console\Contracts\OutputInterface;

final class HelloCommand implements CommandInterface
{
    public function name(): string
    {
        return 'hello';
    }

    public function description(): string
    {
        return 'Display a greeting.';
    }

    public function aliases(): array
    {
        return [];
    }

    public function usage(): string
    {
        return 'hello [name]';
    }

    public function isHidden(): bool
    {
        return false;
    }

    public function execute(
        InputInterface $input,
        OutputInterface $output
    ): int {
        $name = $input->argument(
            0,
            'Developer'
        );

        $output->success(
            "Hello, {$name}."
        );

        return 0;
    }
}

Register it in bootstrap/app.php:

use App\Console\Commands\HelloCommand;

$framework->registerCommand(
    new HelloCommand()
);

Run it:

php bin/console hello Gaspard

Closure-backed commands

Commands may also be registered directly:

use Ivi\Console\Contracts\InputInterface;
use Ivi\Console\Contracts\OutputInterface;

$framework->command(
    name: 'app:environment',
    handler: static function (
        InputInterface $input,
        OutputInterface $output
    ) use ($framework): int {
        $output->info(
            'Environment: '
            . $framework->environment()
        );

        return 0;
    },
    description: 'Display the application environment.',
    aliases: [
        'env',
    ]
);

Run it:

php bin/console app:environment

or:

php bin/console env

Console help

Display all commands:

php bin/console

or:

php bin/console list

Display help for one command:

php bin/console help about

Command help is also available with:

php bin/console about --help

Display the application version:

php bin/console --version

Console entry point

The executable console file is:

bin/console

It:

  • loads the application bootstrap;
  • validates the returned framework instance;
  • starts the framework;
  • executes the requested command;
  • returns the command exit code;
  • displays startup failures safely.

Run it through PHP:

php bin/console

Make it directly executable:

chmod +x bin/console

Then run:

./bin/console

Debug mode

Enable debug mode in .env:

APP_DEBUG=true

When console startup fails, debug mode displays:

  • exception class;
  • source file;
  • source line;
  • stack trace.

Disable it in production:

APP_DEBUG=false

Storage directories

Writable application files are stored in:

storage/

The default directories are:

storage/cache
storage/logs
storage/views

They are automatically created by Composer and by the default application service provider.

Create them manually when necessary:

mkdir -p \
  storage/cache \
  storage/logs \
  storage/views

The contents of these directories are ignored by Git.

Resolving application paths

The framework safely resolves paths relative to the project root.

$configPath = $framework->path(
    'config/app.php'
);

$storagePath = $framework->path(
    'storage'
);

$logsPath = $framework->path(
    'storage/logs'
);

Absolute paths and parent-directory traversal are rejected.

Accessing framework services

Retrieve the application:

$application = $framework->application();

Retrieve the service container:

$container = $framework->container();

Retrieve configuration:

$config = $framework->config();

Retrieve the console:

$console = $framework->console();

Retrieve the framework manager:

$manager = $framework->manager();

Application startup

Start the application manually:

$framework->start();

This performs:

Bootstrap application
Boot service providers

The lifecycle is idempotent. Completed stages are not executed again.

Bootstrap without booting providers:

$framework->bootstrap();

Boot registered providers:

$framework->boot();

Check the lifecycle state:

if ($framework->isBootstrapped()) {
    // Bootstrap operations completed.
}

if ($framework->isBooted()) {
    // Providers completed booting.
}

if ($framework->isStarted()) {
    // Application is fully started.
}

Custom bootstrap operations

Register application initialization logic:

use Ivi\Framework\Contracts\ApplicationInterface;

$framework->bootstrapWith(
    'prepare.uploads',
    static function (
        ApplicationInterface $application
    ): void {
        $path = $application->path(
            'storage/uploads'
        );

        if (!is_dir($path)) {
            mkdir(
                $path,
                0775,
                true
            );
        }
    }
);

Bootstrap operations execute in registration order.

Development server

Once the public HTTP entry point is configured, start PHP's built-in development server:

php -S 127.0.0.1:8000 -t public

Open:

http://127.0.0.1:8000

The built-in PHP server is intended for local development only.

Composer commands

Install dependencies:

composer install

Update dependencies:

composer update

Refresh autoload files:

composer dump-autoload

Validate the project configuration:

composer validate --strict

Production preparation

Before deploying:

composer install \
  --no-dev \
  --optimize-autoloader

Use production environment settings:

APP_ENV=production
APP_DEBUG=false

Ensure the storage directories are writable by the application process:

chmod -R 775 storage

Deployment permissions should be adapted to the server and user configuration.

Extending the application

Application code belongs under:

src/

Recommended namespaces:

App\Console\Commands
App\Http\Controllers
App\Providers
App\Services
App\Repositories
App\Models

Composer maps the App namespace to src:

{
  "autoload": {
    "psr-4": {
      "App\\": "src/"
    }
  }
}

After adding or moving classes, regenerate Composer autoload files:

composer dump-autoload

Design principles

The Ivi application skeleton follows these principles:

  • minimal initial structure;
  • modular framework packages;
  • explicit application bootstrapping;
  • service-provider based composition;
  • dependency-container integration;
  • console-first developer tooling;
  • environment-based configuration;
  • replaceable application services;
  • predictable application lifecycle;
  • no unnecessary application abstractions.

License

Ivi is open-source software released under the MIT License.

Maintainer

Maintained by Gaspard Kirira and Softadastra.

About

The official application skeleton for building modular web and console applications with Ivi.php.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages