GithubHelp home page GithubHelp logo

minphp-bridge's Introduction

Minphp/Bridge

This library allows you to seamlessly take advantage of newer namespaced minPHP libraries while at the same time maintaining backwards compatibility with minPHP 0.x global classes.

Why use this library

This library is intended for projects that are built using minPHP 0.x that want to take advantage of other namespaced minPHP libraries.

Installation

Install via composer:

composer require minphp/bridge

Usage

The bridge requires some information before it's able to initialize some libraries. This is handled by populating and passing in a container that implements Minphp\Container\ContainerInterface.

The following config files in minphp 0.x were removed in minphp 1.0, which is what necessitates populating the container:

  • core.php
  • database.php
  • session.php

minPHP uses the Minphp\Container\Container, which meets this requirement. The following elements are required to be set:

  • minphp.cache array containing:
    • dir string
    • dir_permission int (octal)
    • extension string
    • enabled bool
  • minphp.config array containing:
    • dir string
  • minphp.constants array containing:
    • APPDIR string
    • CACHEDIR string
    • COMPONENTDIR string
    • CONFIGDIR string
    • CONTROLLERDIR string
    • DS string
    • HELPERDIR string
    • HTACCESS bool
    • LANGDIR string
    • LIBDIR string
    • MINPHP_VERSION string
    • MODELDIR string
    • PLUGINDIR string
    • ROOTWEBDIR string
    • VENDORDIR string
    • VIEWDIR string
    • WEBDIR string
  • minphp.language array containing:
    • default string 'en_us'
    • dir string
    • pass_through bool
  • minphp.mvc array containing the following keys:
    • default_controller string
    • default_structure string
    • default_view string
    • error_view string
    • view_extension string
    • cli_render_views bool
    • 404_forwarding * bool*
  • minphp.session array containing the following keys (all optional):
    • db array containing:
      • tbl string The session database table
      • tbl_id string The ID database field
      • tbl_exp string The expiration database field
      • tbl_val string The value database field
      • ttl int The session time-to-live, in seconds, relative to current server time (should be set to the same value as the other TTLs, e.g., 'max(ttl, cookie_ttl)' to correctly sync client and server session expirations)
    • ttl int Number of seconds to keep a session alive.
    • cookie_ttl int Number of seconds to keep long storage cookie alive.
    • session_name string Name of the session.
    • session_httponly bool True to enable HTTP only session cookies.
  • cache Minphp\Cache\Cache
  • view View As a factory (new instance each time)
  • loader Loader
  • pdo PDO

Creating and Using the Container

First create a new config file called services.php that will be used to define our service providers.

Each service is defined as the fully qualified class name. It can be whatever you want as long as it can be properly autoloaded.

/config/services.php

<?php
return [
    'App\\ServiceProviders\\MinphpBridge'
];

Next, create the service provider that matches the one we added to services.php.

/app/ServiceProviders/MinphpBridge.php

Note: You can auotload classes in this directory by defining the namespace in your composer.json file under the "autoload" section like so:

    "autoload": {
        "psr-4": {
            "App\\ServiceProviders\\": "app/ServiceProviders/"
        }
    }
<?php
namespace App\ServiceProviders;

use Pimple\ServiceProviderInterface;
use Pimple\Container;
use Cache;
use View;
use Loader;
use PDO;
use Configure;

class MinphpBridge implements ServiceProviderInterface
{
    private $container;

    /**
     * {@inheritdoc}
     */
    public function register(Container $container)
    {
        $this->container = $container;
        $this->registerCache();
        $this->registerConfig();
        $this->registerConstants();
        $this->registerLanguage();
        $this->registerMvc();
        $this->registerSession();

        $container->set('cache', function ($c) {
            return Cache::get();
        });

        $container->set('view', $container->factory(function ($c) {
            return new View();
        }));

        $container->set('loader', function ($c) {
            $constants = $c->get('minphp.constants');
            $loader = Loader::get();
            $loader->setDirectories([
                $constants['ROOTWEBDIR'] . $constants['APPDIR'],
                'models' => $constants['MODELDIR'],
                'controllers' => $constants['CONTROLLERDIR'],
                'components' => $constants['COMPONENTDIR'],
                'helpers' => $constants['HELPERDIR'],
                'plugins' => $constants['PLUGINDIR']
            ]);

            return $loader;
        });

        $container->set('pdo', function ($c) {
            Configure::load('database');
            $dbInfo = Configure::get('Database.profile');
            return new PDO(
                $dbInfo['driver'] . ':dbname=' . $dbInfo['database']
                . ';host=' . $dbInfo['host'] . (
                    isset($dbInfo['port'])
                    ? ':' . $dbInfo['port']
                    : ''
                ),
                $dbInfo['user'],
                $dbInfo['pass']
            );
        });
    }

    private function registerCache()
    {
        $this->container->set('minphp.cache', function ($c) {
            return [
                'dir' => $c->get('minphp.constants')['CACHEDIR'],
                'dir_permissions' => 0755,
                'extension' => '.html',
                'enabled' => true
            ];
        });
    }

    private function registerConfig()
    {
        $this->container->set('minphp.config', function ($c) {
            return [
                'dir' => $c->get('minphp.constants')['CONFIGDIR']
            ];
        });
    }

    private function registerConstants()
    {
        $this->container->set('minphp.constants', function ($c) {
            $rootWebDir = realpath(dirname(dirname(dirname(__FILE__))))
                . DIRECTORY_SEPARATOR;

            $appDir = 'app' . DIRECTORY_SEPARATOR;
            $htaccess = file_exists($rootWebDir . '.htaccess');

            $script = isset($_SERVER['SCRIPT_NAME'])
                ? $_SERVER['SCRIPT_NAME']
                : (
                    isset($_SERVER['PHP_SELF'])
                    ? $_SERVER['PHP_SELF']
                    : null
                );

            $webDir = (
                !$htaccess
                ? $script
                : (
                    ($path = dirname($script)) === '/'
                    || $path == DIRECTORY_SEPARATOR ? '' : $path
                )
            ) . '/';

            if ($webDir === $rootWebDir) {
                $webDir = '/';
            }


            return [
                'APPDIR' => $appDir,
                'CACHEDIR' => $rootWebDir . 'cache' . DIRECTORY_SEPARATOR,
                'COMPONENTDIR' => $rootWebDir . 'components' . DIRECTORY_SEPARATOR,
                'CONFIGDIR' => $rootWebDir . 'config' . DIRECTORY_SEPARATOR,
                'CONTROLLERDIR' => $rootWebDir . $appDir . 'controllers' . DIRECTORY_SEPARATOR,
                'DS' => DIRECTORY_SEPARATOR,
                'HELPERDIR' => $rootWebDir . 'helpers' . DIRECTORY_SEPARATOR,
                'HTACCESS' => $htaccess,
                'LANGDIR' => $rootWebDir . 'language' . DIRECTORY_SEPARATOR,
                'LIBDIR' => $rootWebDir . 'lib' . DIRECTORY_SEPARATOR,
                'MINPHP_VERSION' => '1.0.0',
                'MODELDIR' => $rootWebDir . $appDir . 'models' . DIRECTORY_SEPARATOR,
                'PLUGINDIR' => $rootWebDir . 'plugins' . DIRECTORY_SEPARATOR,
                'ROOTWEBDIR' => $rootWebDir,
                'VEDNORDIR' => $rootWebDir . 'vendors' . DIRECTORY_SEPARATOR,
                'VIEWDIR' => $rootWebDir . $appDir . 'views' . DIRECTORY_SEPARATOR,
                'WEBDIR' => $webDir
            ];
        });
    }

    private function registerLanguage()
    {
        $this->container->set('minphp.language', function ($c) {
            return [
                'default' => 'en_us',
                'dir' => $c->get('minphp.constants')['LANGDIR'],
                'pass_through' => false
            ];
        });
    }

    private function registerMvc()
    {
        $this->container->set('minphp.mvc', function ($c) {
            return [
                'default_controller' => 'main',
                'default_structure' => 'structure',
                'default_view' => 'default',
                'error_view' => 'errors',
                'view_extension' => '.pdt',
                'cli_render_views' => false,
                '404_forwarding' => false
            ];
        });
    }

    private function registerSession()
    {
        $this->container->set('minphp.session', function ($c) {
            return [
                'db' => [
                    'tbl' => 'sessions',
                    'tbl_id' => 'id',
                    'tbl_exp' => 'expire',
                    'tbl_val' => 'value'
                ],
                'ttl' => 1800, // 30 mins
                'cookie_ttl' => 604800, // 7 days
                'session_name' => 'sid',
                'session_httponly' => true
            ];
        });
    }
}

Updating init.php

Update /lib/init.php so it looks like the following:

<?php
error_reporting(-1);

// include autoloader
require_once dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR
    . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php';

// Fetch available services
$services = require dirname(__DIR__) . DIRECTORY_SEPARATOR . 'config'
    . DIRECTORY_SEPARATOR . 'services.php';

// Initialize
$container = new Minphp\Container\Container();

// Set services
foreach ($services as $service) {
    $container->register(new $service());
}

// Run bridge
$bridge = Minphp\Bridge\Initializer::get();
$bridge->setContainer($container);
$bridge->run();

// Set the container
Configure::set('container', $container);

return $container;

Removing Unused Files

With this bridge in place, you can now remove minPHP 0.x files that are no longer required in your project.

Remove the following directories and files:

  • components/acl/
  • components/input/
  • components/record/
  • components/session/
  • helpers/date/
  • helpers/form/
  • helpers/html/
  • helpers/javascript/
  • helpers/pagination/
  • helpers/xml/
  • config/core.php
  • config/database.php (unless you used it in your MinphpBridge service provider)
  • config/session.php
  • lib/ - except the modified init.php file

Q: Why do we keep the init.php file?

A: Because index.php loads it in minPHP 0.x, and we're maintaining backwards compatibility. If init.php isn't loaded anywhere else, then you could put its contents in another file and update your index.php file to load that file instead. It's up to you.

minphp-bridge's People

Contributors

abdyfranco avatar clphillips avatar jreissmueller avatar tysonphillips avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

minphp-bridge's Issues

Fatal errors produce error 'Undefined variable: e'

Fatal errors should be handled by the bridge gracefully, but always generate the text 'Undefined variable: e' on a white page.

This appears to occur because UnknownException::setFatalErrorHandler calls Dispatcher::raiseError with an undefined variable $e, rather than an instance of Exception.

Form CSRF tokens cannot verify successfully without specifying token key

Minphp/Form was updated to not be backward-compatible when no token key is set. This is the default behavior. i.e., when instantiating a new Form object and using it without setting a CSRF token key. This behavior works fine in minPHP 0.x for the form component, but no longer.

The Minphp/Form was updated to change a null token key to either the form action property, or the request URI, during form creation (Form::create) and verifying (Form::verifyCsrfToken), respectively.

An issue arises where the form token key cannot be verified, such as:

  • JavaScript changing the form action attribute after form creation
  • AJAX requests POSTed to a URL whose CSRF token cannot be re-used from an existing form on the page due to a change in the form's action

For backward compatibility, the bridge's Form helper should set a CSRF token key on construction.

Dispatcher may try to call preAction method on non-controller

The Dispatcher class assumes controllers have a preAction method. However, it simply checks whether the given controller is a class. There could be a matching class loaded that is not a controller, and thus does not have a preAction method. The result is an exception:

Call to undefined method MyClass::preAction()

The controller should have the preAction method to be considered a controller and should throw an exception otherwise.

Set exceptions to view when raising an error

The Dispatcher currently only sets an exception's error message to the error view when raising an error. While this is useful, it does not give us any information to work with in determining offending file, or line of code, etc.

The error itself (the exception) should be set to the error view. It should be up to the view itself what information to display.

php7 errors are not supported

php7 adds the Error class as well as Exception, so both could be thrown.

The bridge explicitly requires the Exception type as an argument, which is incompatible with php7 in cases where Error could be thrown. The Exception argument type must be dropped and both Exception (php5) and Throwable (php7) must be supported in order to support both php5 and php7.

Use minphp/record v3

v3 of minphp/record is being released in order to support customizable table character set and collation, as well as defaulting to utf8mb4. minphp/bridge should be updated to pull in this version.

Plugins controllers are not autoloaded from the correct location

When a plugin controller in plugins/myplugin/controllers/myplugin is requested via the URI myplugin/myplugin the autoloader attempts to load the class from controllers/myplugin.php instead of plugins/myplugin/controllers/myplugin.php

This is because Dispatcher::dispatch() initializes the class as just the class name Myplugin, so there's no plugin context available there.

Dispatcher requires controller method exist

The Dispatcher makes a call to method_exists($ctrl, $action) to check whether the given action exists on the controller, otherwise it throws an exception. However, this does not allow for controllers to implement the magic methods __call or __callStatic to handle actions/autoloading dynamically.

Consider updating this to check whether the method_exists or is_callable.

Note: if a class implements __call then is_callable will always be true, which I think is the desired behavior, i.e., route the action to the specific controller to determine what it should do via __call.

Bridge fails to autoload its own components before minphp 0.x's

The bridge creates a wrapper for components and helpers from minphp for backward compatibility with existing applications that use minphp 0.x. The components and helpers used by minphp no longer need to be included in minphp 0.x due to this bridge.

The issue is that minphp 0.x components, like the Form, Html, or Session, and the bridge's wrappers for these are only used if the source files do not exist. This is opposite from the desired behavior. i.e., use the bridge's corresponding wrappers even if the minphp 0.x components exist on the filesystem, and only fallback to the old minphp 0.x components if the bridge wrappers don't exist (which should never happen anyway).

Support minphp/session v1.1 session cookie management

minphp/session v1.1.0 was added to support setting cookies--functionality previously possible in minphp 0.x.

The bridge should take advantage of minphp/session v1.1 to implement backward-compatible behavior with minphp 0.x.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.