phossa2/framework

A modern PHP framework built-upon configuration, dependency injection and middlewares.

dev-master / 2.1.x-dev 2016-10-11 09:01 UTC

This package is not auto-updated.

Last update: 2024-04-13 16:46:04 UTC


README

phossa2/framework is a modern PHP framework built-upon configuration, dependency injection and middlewares.

It requires PHP 5.4, supports PHP 7.0+ and HHVM. It is compliant with PSR-1, PSR-2, PSR-3, PSR-4, and other proposed PSR standards.

Create the project

Install via the composer utility.

# cd installation_dir/
# composer create-project phossa2/framework PROJECT

Directory structure

phossa2/framework is delivered with the single server installation directory structure. It also can be restructured to fit different requirements by modifying directory settings in the .env file.

  • Single server installation

    Default framework distribution is for a single server installation.

    |--- .env                             the environment file
    +--- PROJECT/                         the project directory
          |--- phossa2                    the utility script
          |--- app/                       app installation dir
          |--- config/                    where all the config files located
          |--- plugin/                    where all the plugins installed
          |--- public/                    where public stuff located
          |      |--- asset/
          |      +--- index.php           single public entry
          |--- runtime/                   runtime related stuff
          |      |--- local/              host-specific storage
          |      |      |--- cache
          |      |      +--- session
          |      +--- log/                log file directory
          |--- system/                    system files
          |      +--- bootstrap.php       bootstrap file
          +--- vendor/                    third-party libs
    
  • Multiple servers installation

    The framework can also be installed across multiple servers to provide capabilities such as load balancing, central app managment (NFS mounted) etc.

    |--- .env                             host-specific environments
    |--- local                            host-specific local storage
    |     |--- cache/
    |     +--- session/
    |
    |--- PROJECT/                         shared among servers (NFS mounted)
    |     |--- phossa2
    |     |--- app/
    |     |--- config/
    |     |--- plugin/
    |     |--- public/
    |     |--- system/
    |     +--- vendor/
    |
    +--- runtime/                         shared runtime stuff (NFS mounted)
          |--- log/host1                  host-specific log dir
          |--- upload/                    upload dir
          +--- static/                    generated static html files

Execution path

  • Application execution path

    1. public/index.php

    Single app entry point. Load system/bootstrap.php file and then process the app middleware queue defined in config/middleware.php

    <?php
    // public/index.php
    
    // Load bootstrap file
    require dirname(__DIR__) . '/system/bootstrap.php';
    
    // execute the main middleware queue
    $response = Service::middleware()->process(
        ServerRequestFactory::fromGlobals(),
        new Response()
    );
    1. system/bootstrap.php

    Required by public/index.php or phossa2 utility script. Bootstrap all required stuff, including

    • set basic environments.

    • start autloading.

    • load other environments from .env file.

    • start $config and $container which read configs from the config/ directory.

    1. .env

    Environment file installled at one level upper of PROJECT directory. This file is host specific and may differ on different servers.

    See phossa2/env and phossa2/config for detail.

    • To change app environment

      Change the value of PHOSSA2_ENV to implement different servers, such as production server, dev server or staging servers.

    • To restructure the framework

      By changing directory settings in this file, user may be able to restructure the framework.

    1. start $config and $container
    • configure

      Configurations are grouped into files and located in the directory config/.

      See phossa2/config for detail.

    • container

      phossa2/di provides a PSR-11 compliant container implementation built upon phossa2/config.

      Container objects are configured in config/di.php or scattered in the 'di' section of different files such as config/db.php.

      A service locator Phossa2\Di\Service is also provided.

      The container object is available as Service::container(). The configuration object is available as Service::config().

      use Phossa2\Di\Service;
      
      $config = Service::config();
      $container = Service::container();
      
      // get the db configuration array
      $db_conf = $config->get('db');
      
      // get the db object
      $db = $container->get('db');
      
      // or get from locator
      $db = Service::db();
    1. Process the app middleware queue

    Middlewares are defined in config/middleware.php.

  • Console script execution path

    1. phossa2 utility script

    Single utility entry point. Load system/bootstrap.php file and then process command line arguments

    1. Common line arguments

    Common line arguments processed. And then look for controller/action pairs in the 'system/Console/' and 'app/Console/' directories for specific actions.

Configuration driven framework

phossa2/framework is a configruation driven framework. Most of the objects, utilities are defined in config files under the config/ directory. Objects are generated automatically by the DI container and avaiable via Service::objectId().

For example, the database connection is defined in config/db.php as follows,

use Phossa2\Db\Driver\Pdo\Driver as Pdo_Driver;

// config/db.php
return [
    // PDO driver classname
    'driver.pdo.class' => Pdo_Driver::getClassName(),

    // connect conf
    'driver.pdo.conf' => [
        'dsn' => 'mysql:dbname=test;host=127.0.0.1;charset=utf8',
    ],

    // container section
    'di' => [
        // ${#db}
        'db' => [
            'class' => '${db.driver.pdo.class}',
            'args' => ['${db.driver.pdo.conf}'],
        ],
    ],
];

The last section di equals to defining a $db in the container

$db = new Pdo_Driver(['dsn' => '...']);
$container->set('db', $db);

To utilize the database connection in your code, you may either inject it in another container object configuration file.

// config/article.php
return [
    'class' => MyArticle::getClassName();

    // ${#article} in container
    'di' => [
        'article' => [
            'class' => '${article.class}',
            'args' => ['${#db}'] // inject $db
        ]
    ]
];

Or use it explicitly with the service locator,

use Phossa2\Di\Service;

// get db
$db = Service::db();

$article = new MyArticle($db);

Complicated db configurations can be found in config/production/db.php which uses a db connection manager with a pool of a read-write connection and couple of read-only connections.

use Phossa2\Db\Manager as Db_Manager;
use Phossa2\Db\Driver\Pdo\Driver as Pdo_Driver;

// config/production/db.php
return [
    // driver manager
    'manager.class' => Db_Manager::getClassName(),

    // more connect confs
    'driver.pdo.conf2' => [
        'dsn' => 'mysql:dbname=test;host=127.0.0.2;charset=utf8',
    ],

    'driver.pdo.conf3' => [
        'dsn' => 'mysql:dbname=test;host=127.0.0.3;charset=utf8',
    ],

    // callback to get a db from db manager with tagname
    'callable.getdriver' => function($dbm, $tag) {
        return $dbm->getDriver($tag);
    },

    // container section
    'di' => [
        // ${#dbm}
        'dbm' => [
            'class' => '${db.manager.class}',
            'methods' => [
                ['addDriver', ['${#db1}', 1]],
                ['addDriver', ['${#db2}', 5]],
                ['addDriver', ['${#db3}', 5]],
            ],
        ],

        // ${#db1}
        'db1' => [
            'class' => '${db.driver.pdo.class}',
            'args' => ['${db.driver.pdo.conf}'],
            'methods' => [
                ['addTag', ['RW']]
            ]
        ],

        // ${#db2}
        'db2' => [
            'class' => '${db.driver.pdo.class}',
            'args' => ['${db.driver.pdo.conf2}'],
            'methods' => [
                ['addTag', ['RO']]
            ]
        ],

        // ${#db3}
        'db3' => [
            'class' => '${db.driver.pdo.class}',
            'args' => ['${db.driver.pdo.conf3}'],
            'methods' => [
                ['addTag', ['RO']]
            ]
        ],

        // ${#dbro} read only driver (round-robin)
        'dbro' => [
            'class' => '${db.callable.getdriver}',
            'args' => ['${#dbm}', 'RO'],
            'scope' => Container::SCOPE_SINGLE,
        ],

        // ${#dbrw} readwrite driver (round-robin if any)
        'dbrw' => [
            'class' => '${db.callable.getdriver}',
            'args' => ['${#dbm}', 'RW'],
            'scope' => Container::SCOPE_SINGLE,
        ],

        // ${#db} either RW or RO
        'db' => [
            'class' => '${db.callable.getdriver}',
            'args' => ['${#dbm}', ''],
            'scope' => Container::SCOPE_SINGLE,
        ],
    ],
];

The previous configruations equal to the following code,

// different db connectors
$db1 = (new Pdo_Driver($conf ))->addTag('RW');
$db2 = (new Pdo_Driver($conf2))->addTag('RO');
$db3 = (new Pdo_Driver($conf3))->addTag('RO');

// db manager
$dbm = (new Db\Manager\Manager())
    ->addDriver($db1, 1)    // readwrite, factor 1
    ->addDriver($db2, 5)    // read_only, factor 5
    ->addDriver($db3, 5)    // read_only, factor 5

// get a readonly connection (round robin)
$dbro = $dbm->getDriver('RO');

// get a readwrite connection
$dbrw = $dbm->getDriver('RW');

// get a db connection (either RW or RO)
$db = $dbm->getDriver('');

Middleware driven framework

phossa2/framework is not a pure MVC structure but a middleware-centric framework. For middleware runner implementation, please see phossa2/middleware.

Different middleware queues are defined in config/middleware.php.


Routing

Routes are handled by Phossa2\Middleware\Middleware\Phossa2RouteMiddleware. See phossa2/middleware, phossa2/route for detail.

Route dispatcher $dispatcher is defined in config/route.php. It will be injected into the main middleware queue when processing reaches the Phossa2RouteMiddleware.

Different routes should be defined in config/route/*.php files. For example,

// route/50_admin.php
$ns = "App\\Controller\\"; // controller namespace

return [
    'prefix' => '/admin/',
    'routes' => [
        // resolve to ['App\Controller\AdminController', 'defaultAction']
        '/admin/{action:xd}/{id:d}' => [
            'GET,POST',                     // http methods,
            [$ns . 'Admin', 'default'],     // handler,
            ['id' => 1]                     // default values
        ],
    ]
];

Note: 50_ in the route filename is for sorting purpose.

Application programming

  • Do it a simple way

    You may just start programming in the app/ directory where phossa2/app-skeleton is already installed during the project creation.

  • Do it a nice way

    1. Git clone app-skeleton to your local directory.

    2. Add your own stuff to the cloned application skeleton.

    3. Remove the initially installed app-skeleton from the project

    # cd PROJECT/
    # composer remove phossa2/app-skeleton
    1. Install your app into the PROJECT
    • If your app is on the git

      Add the following lines to your PROJECT/composer.json

      "repositories": [
          {
              "type":"package",
              "package": {
                  "name": "my/app",
                  "version": "master",
                  "source": {
                      "url": "https://github.com/my/app.git",
                      "type": "git",
                      "reference":"master"
                  }
              }
          }
      ]
      
    • If your app is just a zip.

      Add the following lines to your PROJECT/composer.json

      "repositories": [
          {
              "type": "package",
              "package": {
                  "name": "my/app",
                  "version": "master",
                  "dist": {
                      "type": "zip",
                      "url": "http://mydomain.com/downloads/app-1.4.zip",
                      "reference": "master"
                  }
              }
          }
      ]
      
    • then install the app via composer require or composer update

      # cd PROJECT/
      # composer require my/app

License

MIT License