axy/env

Abstracting access to the environment

0.2.0 2016-06-24 12:33 UTC

This package is auto-updated.

Last update: 2024-12-08 01:58:00 UTC


README

Abstracting access to the environment (PHP).

Latest Stable Version Minimum PHP Version Build Status Coverage Status License

  • The library does not require any dependencies (except composer packages).
  • Tested on PHP 5.4+, PHP 7, HHVM (on Linux), PHP 5.5 (on Windows).
  • Install: composer require axy/env.
  • License: MIT.

Documentation

The library provides the abstract layer for access to the environment. The environment is

  • The current time and timezone
  • PHP runtime settings
  • Input data ($_GET, $_POST ...)
  • Server data ($_SERVER, $_ENV)
  • HTTP headers and cookies
  • and etc... any parameter whose modification may affect other parts of the system

If some code has direct access to super-global arrays or calls functions like time() or header() then it code becomes tied to the environment. This code is difficult to test and configure.

The library provides the environment wrapper (an instance of axy\env\Env class). The application code gets this wrapper from the outside and works with the environment via it. By default the wrapper just delegates requests to standard functions. But this behaviour can be redefined (for test or an other purpose).

Example

use axy\env\Factory;

class Service
{
    public function __construct($env = null)
    {
        $this->env = Factory::create($env);
    }

    public function action()
    {
        $timeOfAction = $this->env->getCurrentTime();
        // ...
    }

    private $env;
}

// ...

$service = new Service();
$service->action();

By default will be used the standard environment and $this->env->getCurrentTime() returns the current time. But this behaviour can be changed.

$service = new Service(['time' => '2014-11-04 10:11:12']);
$service->action(); // the service will receive the specified time

Config

The environment wrapper with standard behaviour:

use axy\env\Env;

$env = new Env();

The non-standard behavior is specified in the config:

$config = [
    'time' => '2015-11-04 11:11:11',
    'get' => [
        'id' => 5,
    ],
];

$env = new Env($config);

The config specified via an array (as above) or via an instance of axy\env\Config:

$config = new Config();
$config->time = '2015-11-04 11:11:11';
$config->get = ['id' => 5];

$env = new Env($config);

An array is simply, but the object supports autocomplete in IDE.

Specific parameters of the config are described in the relevant sections below.

Cloning
$config = new Config();
$config->time = '2015-11-04 11:11:11';
$env = new Env($config);

$config->time = '2011-02-03 10:10:10';
echo date('j.m.Y', $env->getCurrentTime()); // 4.11.2015, config is cloned

Current Time

Check the current time (returns a timestamp):

$env->getCurrentTime(void): int

Parameter time specified the current time. It can be an int or a numeric string (timestamp) or a string for strtotime.

  • 1234567890 - a timestamp (2009-02-14 02:31:30)
  • "1234567890" - similarly
  • "2015-11-04 10:11:12" - a time in the current timezone
  • "2015-11-04" - "2015-11-04 00:00:00"
  • "+1 month" - relative time for strtotime()
$config->time = '2015-11-04 00:01:02';
$env = new Env($config);

echo date('j.m.Y', $env->getCurrentTime()); // 4.11.2015
Changing time

$env->getCurrentTime() in the above example always returns the same time. For long-lived scenarios, the time change can be important.

/**
 * Cron daemon
 * Once an hour to kill, not to eat a lot of memory
 */

$startTime = time();

while (time() - $startTime() < 3500) {
    step();
    sleep(5);
}

If specified timeChanging then the time will be changing.

$env = new Env([
    'time' => '1980-01-02 11:20:30',
    'timeChanging' => true,
]);

echo date('H:i:s', $env->getCurrentTime()).PHP_EOL; // 11:20:30
sleep(7);
echo date('H:i:s', $env->getCurrentTime()).PHP_EOL; // 11:20:37
Custom function instead time()

If time is not specified getCurrentTime() calls a wrapper of the global function time() (see below section "Global Functions"). It can override.

$config = [
    'functions' => [
        'time' => 'myOwnTimeImplementation',
    ],
];

Super-Globals

The super-globals arrays $_SERVER, $_ENV, $_GET, $_POST, $_REQUEST, $_COOKIE, $_FILES available via

  • $env->server
  • $env->env
  • $env->get
  • $env->post
  • $env->request
  • $env->cookie
  • $env->files

Their can override in the config:

$config = [
    'get' => ['x' => 1],
];
$env = new Env($config);

$env->get['x']; // 1
$env->post['x']; // $_POST['x']

Global Functions

Magic __call delegates to global functions.

$env->strlen('string'); // 6
$env->header('Content-Type: text/plain'); // Send header

Overriding:

$config = [
    'functions' => [
        'header' => function ($header) {
            // save header to a local storage
        },
    ],
];

$env = new Env();

$env->header('Content-Type: text/plain'); // The header will not be sent

Checking the existence of functions $env->isFunctionExists(string $name):bool.

echo() can also be overridden.

Example

Function getallheaders() not exist in all environments.

if ($env->isFunctionExists('getallheaders')) {
    return $env->getallheaders();
}

Override:

$config = [
    'functions' => [
        'getallheaders' => function () {
            if (function_exists('getallheaders')) {
                return getallheaders();
            } else {
                return parseServerVarsForHeaders();
            }
        },
    ],
];

// now $env->getallheaders() always available

Or

$config = [
    'functions' => [
        'getallheaders' => null, // never
    ],
];
Functions list

It is intended for functions that access the environment. In phpdoc for autocomplete lists the following:

But theoretically it can be used for any function.

Streams

Access to php I/O streams.

$env->streams->stdout->write('Output');

Container $env->streams contains following object:

  • stdin
  • stdout
  • stderr
  • input
  • output

These objects implement interface IStream.

You can create object with the interface IStream and spoof a stream:

$config = [
    'streams' => [
        'stdin' => new MyStream(),
    ],
];
$env = new Env($config);

You can use classes Stream and StreamMock:

Stream is wrapper on a stream resource.

$config = [
    'streams' => [
        'stdin' => new \axy\env\Stream(fopen('/my/file.txt')),
    ],
];
$env = new Env($config);
StreamMock

StreamMock is a wrapper on a string.

$mock = new \axy\env\StreamMock('content');
$mock->read(3); // "con"
$mock->read(); // "tent"
$mock->write('!');
$mock->setPosition(0);
$mock->read(); // "content!"

In addition to the methods of IStream, StreamMock supports following methods:

  • setContent(string $content [, int $position])
  • setPosition(int $position)
  • getContent(): string
  • getPosition(): int

Factory

use axy\env\Factory;

$env = Factory::getStandard();

The method getStandard() returns an env wrapper for standard behaviour.

The method create($config) creates a wrapper with overriding behaviour. It can taken:

an array

$config = [
    'time' => 123456,
];

$env = Factory::create($config);

a Config instance

$config = new Config();
$config->time = 123456,

$env = Factory::create($config);

or a Env instance itself

$env1 = new Env([
    'time' => 123456,
]);

$env2 = Factory::create($env1); // $env1 === $env2

By default (NULL) specifies the standard wrapper.

The target service can take wrapper in different formats and not to deal with them.

use axy\env\Factory;

class Service
{
    public function __construct($env = null)
    {
        $this->env = Factory::create($env);
    }

    // ...
}

$service = new Service(['time' => '2011-01-01']);

Exceptions

The library can throw the following exceptions:

  • axy\errors\InvalidConfig
    • Unknown fields in a config (['time' => 12345, 'unknown' => 67890])
    • Wrong type of fields (['server' => 5], must be an array)
    • Wrong data in fields ([time => 'wrong time string']).
    • Function wrapper is not callable (detected during a call).
  • axy\errors\FieldNotFound
    • Field not found (echo $env->unknown;).
    • Function not found (echo $env->unknown(5);).
  • axy\errors\Disabled
    • Function disabled in the config
  • axy\errors\ContainerReadOnly
    • $env->server = [] or unset($env->server).