<?php

namespace RocknRoot\StrayFw\Database\Provider;

use RocknRoot\StrayFw\Exception\BadUse;

/**
 * Enum representation parent class for all providers.
 *
 * @abstract
 *
 * @author Nekith <nekith@errant-works.com>
 */
abstract class Enum
{
    /**
     * Keys and values as an associative array.
     *
     * @var array
     */
    protected static $array;

    /**
     * Enum value.
     *
     * @var string
     */
    protected $value;

    /**
     * Creates a new enum with specified value.
     *
     * @param string $v
     *
     * @throws BadUse if $v is not recognized as a possible value
     */
    public function __construct(string $v)
    {
        if (static::isValid($v) === false) {
            throw new BadUse('"' . $v . '" is not recognized as a possible value');
        }
        $this->value = $v;
    }

    /**
     * Get current enum value.
     *
     * @return string
     */
    public function getValue() : string
    {
        return $this->value;
    }

    /**
     * Return current enum value.
     *
     * @return string
     */
    public function __toString() : string
    {
        return $this->value;
    }

    /**
     * Set current enum value.
     *
     * @param string $v new value
     * @return string
     */
    public function setValue(string $v) : string
    {
        if (static::isValid($v) === false) {
            throw new BadUse('"' . $v . '" is not recognized as a possible value');
        }
        $this->value = $v;
    }

    /**
     * Return keys and values as an associative array.
     *
     * @return array
     */
    public static function toArray() : array
    {
        if (static::$array == null) {
            $ref = new \ReflectionClass(static::class);
            $consts = $ref->getConstants();
            static::$array = array();
            foreach ($consts as $key => $value) {
                if (stripos($key, 'VALUE_') === 0) {
                    static::$array[$key] = $value;
                }
            }
        }

        return static::$array;
    }

    /**
     * Check if a value is a possible value for this enum.
     *
     * @param string $value
     * @return bool true if value is recognized
     */
    public static function isValid(string $value) : bool
    {
        return in_array($value, static::toArray());
    }
}


<?php declare(strict_types=1);

require './vendor/autoload.php';

use RPQ\Server\AbstractJob;
use Swift_Mailer;
use Swift_Message;
use Swift_SmtpTransport;

/**
 * This class serves as an example for how to send a single email
 */
final class EmailJob extends AbstractJob
{
    /**
     * Sends a single email
     *
     * `$args` is an array of details pushed into Redis. In this example `$args` has the following
     * structure
     * @param array $args = [
     *      'smtp' => [
     *          'host' => '127.0.0.1',
     *          'port' => 25
     *          'username' => 'smtp_username',
     *          'password' => 'smtp_password'
     *      ],
     *      'subject' => 'Hello Doctor!',
     *      'message' => 'What are you doing for Christmas?',
     *      'from' => [
     *          'email' => 'clara.oswald@tardis.io',
     *          'name' => 'Clara Oswald
     *      ],
     *      'to' => [
     *          'email' => 'doctor@tardis.io',
     *          'name' => 'Doctor'
     *      ]
     * ];
     * @return int
     */
    public function perform(array $args = []): int
    {
        // Create a transport
        $transport = new Swift_SmtpTransport(
            $args['smtp']['host'],
            $args['smtp']['port']
        );
        $transport->setUsername($args['smtp']['username']);
        $transport->setPassword($args['smtp']['password']);

        // Create a message
        $mailer = new Swift_Mailer($transport);
        $message = (new Swift_Message($args['subject']))
            ->setFrom([$args['from']['email'] => $args['from']['email']])
            ->setTo([$args['to']['email'] => $args['to']['name']])
            ->setBody($args['message']);

        // Send the message
        $result = $mailer->send($message);

        // The email was successfully sent if $mail->send returns 1, indicating 1 email was sent
        return $result === 1;
    }
}


<?php

namespace RocknRoot\StrayFw\Database\Postgres\Query;

/**
 * Representation class for PostgreSQL condition expressions.
 *
 * @author Nekith <nekith@errant-works.com>
 */
class Condition
{
    /**
     * Logic condition tree.
     *
     * @var mixed
     */
    protected $tree;

    /**
     * Generated SQL code.
     *
     * @var string|null
     */
    protected $sql;

    /**
     * Construct a new condition expression.
     */
    public function __construct($where)
    {
        if (is_array($where) === true) {
            $this->tree = $where;
        } else {
            $this->sql = $where;
        }
    }

    /**
     * Extract the corresponding SQL code.
     *
     * @return string generated SQL code
     */
    public function toSql()
    {
        if (is_string($this->sql) === false) {
            $this->sql = $this->toSqlLevel($this->tree);
        }

        return $this->sql;
    }

    /**
     * Extract the corresponding SQL code, depth level by level.
     *
     * @param  array  $tree one tree level
     * @return string generated SQL code for this level
     */
    protected function toSqlLevel(array $tree) : string
    {
        if (count($tree) == 0) {
            return '';
        }
        $sql = '(';
        reset($tree);
        if (is_numeric(key($tree)) === true) {
            foreach ($tree as $elem) {
                $sql .= $elem . ' AND ';
            }
            $sql = substr($sql, 0, -5);
        } elseif (key($tree) === 'OR') {
            foreach ($tree as $value) {
                if (is_array($value) === true) {
                    $sql .= $this->toSqlLevel($value);
                } else {
                    $sql .= $value;
                }
                $sql .= ' OR ';
            }
            $sql = substr($sql, 0, -4);
        } elseif (key($tree) === 'AND') {
            foreach ($tree as $value) {
                if (is_array($value) === true) {
                    $sql .= $this->toSqlLevel($value);
                } else {
                    $sql .= $value;
                }
                $sql .= ' AND ';
            }
            $sql = substr($sql, 0, -5);
        } else {
            foreach ($tree as $key => $value) {
                $sql .= $key . ' = ' . $value . ' AND ';
            }
            $sql = substr($sql, 0, -5);
        }
        $sql .= ')';

        return $sql;
    }
}


<?php declare(strict_types=1);

namespace RPQ\Server\Process\Handler;

use Amp\Deferred;
use Amp\Failure;
use Amp\Loop;
use Amp\Success;
use Exception;
use Monolog\Logger;
use Symfony\Component\Process\Process;
use Symfony\Component\Process\Exception\ProcessFailedException;
use RPQ\Client;
use RPQ\Queue;
use RPQ\Server\Process\Dispatcher;
use RPQ\Server\Process\Handler\JobHandler;

final class SignalHandler
{
    /**
     * @var Client $client
     */
    private $client;

    /**
     * @var Logger $this->logger
     */
    private $logger;

    /**
     * @var array $config
     */
    private $config;

    /**
     * @var array $args
     */
    private $args = [
        'configFile' => ''
    ];

    /**
     * @var string $queue
     */
    private $queue;

    /**
     * @var Dispatcher $process
     */
    private $dispatcher;

    /**
     * @var JobHandler $jobHandler
     */
    private $jobHandler;

    /**
     * @var array $signals
     */
    private $signals = [
        SIGTERM => 'terminate',
        SIGINT => 'terminate',
        SIGQUIT => 'quit',
        SIGHUP => 'reload',
        SIGUSR1 => 'reserved',
        SIGUSR2 => 'reserved'
    ];

    private $handlers = [];

    /**
     * Constructor
     * @param Dispatcher $dispatcher
     */
    public function __construct(Dispatcher &$dispatcher)
    {
        $this->dispatcher = $dispatcher;
        $this->logger = $this->dispatcher->getLogger();
        $this->client = $this->dispatcher->getClient();
        $this->config = $this->dispatcher->getConfig();
        $this->queue = $this->dispatcher->getQueue();
        $this->args = $this->dispatcher->getArgs();

        $this->jobHandler = new JobHandler(
            $this->client,
            $this->logger,
            $this->queue
        );
    }

    /**
     * Returns signals that should be handled
     * @return array
     */
    public function getSignals()
    {
        return \array_keys($this->signals);
    }

    /**
     * Handles incoming signals
     * @param Dispatcher $processes
     * @param int $signal
     * @return Promise
     */
    public function handle(int $signal)
    {
        // If this signal is already being handled, return a Failure Promise and don't do anything
        if (isset($this->handlers[$signal]) && $this->handlers[$signal] === true) {
            return new Failure(new Exception('Signal is already being processed.'));
        }

        // Indicate that we're handling this signal to block repeat signals
        $this->handlers[$signal] = true;

        // Disable processing of new jobs while we handle the signal
        $this->dispatcher->setIsRunning(false);

        // Call the signal handler from the signal mapping
        $fn = $this->signals[$signal];
        $result = $this->$fn();

        // $result is either going to be `null` or a boolean value
        // If it's `true`, then skip the shutdown sequence
        if ($result === true) {
            unset($this->handlers[$signal]);
            return new Success($result);
        }

        // Wait until all processes have ended before resolving the promise
        $deferred = new Deferred;
        Loop::repeat(1000, function ($watcherId, $callback) use ($deferred, $signal, $result) {
            if (count($this->dispatcher->getProcesses()) === 0) {
                Loop::cancel($watcherId);
                unset($this->handlers[$signal]);
                return $deferred->resolve($result);
            }
        });

        return $deferred->promise();
    }

    /**
     * Placeholder for future signals that we may want to use
     * @return true
     */
    private function reserved()
    {
        $this->logger->info('This signal is reserved for future use.');
        return true;
    }

    /**
     * Fast Shutdown
     *
     * Send CTRL^C (SIGINT) or SIGTERM to initiate a fast shutdown of RPQ
     * During fast shutdown, RPQ will send a SIGKILL signal to all child processes
     * Then push them back onto the priority queue before exiting.
     *
     * This may result in jobs being reprocessed, as the jobs will not have
     * an opportunity to cleanly exit and shutdown themselves.
     *
     * @return null
     */
    private function terminate()
    {
        $this->logger->info('Recieved SIGTERM signal. Running fast shutdown sequence.');
        $this->shutdown(SIGKILL);
        return null;
    }

    /**
     * Graceful Shutdown
     *
     * Send a SIGQUIT to intiate a graceful shutdown of RPQ and all child processes.
     * During graceful shutdown, RPQ will send a SIGTERM signal to all child processes
     * and allow them to opportunity to either finish working, or to cleanly shutdown themselves.
     *
     * Jobs that do not complete, or return a non-zero exit status code will be requeued.
     * Jobs that do complete will not be requeued.
     *
     * @return null
     */
    private function quit()
    {
        $this->logger->info('Recieved SIGQUIT signal. Running graceful shutdown sequence.');
        $this->shutdown(SIGTERM);
        return null;
    }

    /**
     * Graceful reload
     *
     * Runs a self test to verify that the new RPQ configuration is valid.
     * If the configuration is valid, RPQ will spawn a new RPQ instance to begin job processing
     * If the configuration is not valid, RPQ will report the error and will continue processing jobs
     *
     * @return integer
     */
    private function reload()
    {
        $this->logger->info('Recieved SIGHUP signal.');
        $test = $this->spawnRpqInstance(true);

        if ($test === true) {
            $this->logger->debug('Self test passes. Running graceful shutdown sequence.');
            $pid = $this->spawnRpqInstance(false);
            $this->logger->info('New RPQ process has been started.', [
                'pid' => $pid
            ]);
        } else {
            $this->logger->debug('Self test failed. Aborting reload.');
            $this->dispatcher->setIsRunning(true);
        }

        return null;
    }

    /**
     * Executes a self test by running `./rpq queue -t -c <config_file>.
     * This method is blocking until the self test finishes, and the
     * self test will be run within a separate process space.
     *
     * @return mixed
     */
    private function spawnRpqInstance($withTest = false)
    {
        $command = sprintf(
            '%s queue -c %s',
            $_SERVER["SCRIPT_FILENAME"],
            $this->args['configFile']
        );

        if ($withTest) {
            $command = "exec {$command} -t";
        } else {
            $command = "exec {$command} > /dev/null 2>&1 &";
        }

        // Spawns a detached process
        $process = new Process($command);
        $process->disableOutput();
        $process->start();

        if ($withTest) {
            $process->wait();
            $code = $process->getExitCode();

            return $code === 0;
        }

        return yield $process->getPid();
    }

    /**
     * Interal shutdown function
     *
     * @param integer $signal
     * @return void
     */
    private function shutdown($signal = 9)
    {
        // Iterate through all the existing processes, and send the appropriate signal to the process
        foreach ($this->dispatcher->getProcesses() as $pid => $process) {
            $this->logger->debug('Sending signal to process', [
                'signal' => $signal,
                'pid' => $pid,
                'jobId' => $process['id'],
                'queue' => $this->queue->getName()
            ]);

            /**
             * $process['process']->signal($signal)
             * Amp\Process\Process::signal doesn't actually send signals correctly, and is not cross platform
             * Use posix_kill to actually send the signal to the process for handling
             */
            \posix_kill($pid, $signal);

            // If a signal other than SIGKILL was sent to the process, create a deadline timeout and force kill the process if it's still alive after the deadline
            if ($signal !== 9) {
                if ($this->config['deadline_timeout'] !== null) {
                    Loop::delay(((int)$this->config['deadline_timeout'] * 1000), function ($watcherId, $callback) use ($process, $pid) {
                        if ($process['process']->isRunning()) {
                            $this->logger->info('Process has exceeded deadline timeout. Killing', [
                                'pid' => $pid,
                                'jobId' => $process['id'],
                                'queue' => $this->queue->getName()
                            ]);

                            // Send SIGKILL to the process
                            \posix_kill($pid, SIGKILL);
                        }
                    });
                }
            }
        }
    }
}


<?php

namespace RocknRoot\StrayFw\Database\Postgres;

use RocknRoot\StrayFw\Database\Database as GlobalDatabase;
use RocknRoot\StrayFw\Database\Helper;
use RocknRoot\StrayFw\Database\Mapping;
use RocknRoot\StrayFw\Database\Provider\Schema as ProviderSchema;
use RocknRoot\StrayFw\Exception\DatabaseError;
use RocknRoot\StrayFw\Exception\FileNotWritable;
use RocknRoot\StrayFw\Exception\InvalidSchemaDefinition;

/**
 * Schema representation class for PostgreSQL ones.
 * User code shouldn't use this class directly.
 *
 * @author Nekith <nekith@errant-works.com>
 */
class Schema extends ProviderSchema
{
    /**
     * Build data structures.
     *
     * @throws DatabaseError           if a SQL query fails
     * @throws InvalidSchemaDefinition if a model has no field
     * @throws InvalidSchemaDefinition if an enum-typed field has no values defined
     */
    public function build()
    {
        $mapping = Mapping::get($this->mapping);
        $definition = $this->getDefinition();
        $database = GlobalDatabase::get($mapping['config']['database']);

        foreach ($definition as $modelName => $modelDefinition) {
            if (isset($modelDefinition['links']) === false) {
                continue;
            }
            $tableName = null;
            if (isset($modelDefinition['name']) === true) {
                $tableName = $modelDefinition['name'];
            } else {
                $tableName = Helper::codifyName($this->mapping) . '_' . Helper::codifyName($modelName);
            }
            $query = $database->getMasterLink()->query('SELECT COUNT(*) as count FROM pg_class WHERE relname = \'' . $tableName . '\'');
            $result = $query->fetch(\PDO::FETCH_ASSOC);
            if ($result['count'] != 0) {
                foreach ($modelDefinition['links'] as $keyName => $keyDefinition) {
                    $statement = Mutation\DeleteForeignKey::statement($database, $tableName, $keyName);
                    if ($statement->execute() == false) {
                        throw new DatabaseError('db/build : ' . print_r($statement->errorInfo(), true));
                    }
                }
            }
        }

        foreach ($definition as $modelName => $modelDefinition) {
            if (isset($modelDefinition['type']) === false || $modelDefinition['type'] === 'model') {
                $tableName = null;
                if (isset($modelDefinition['name']) === true) {
                    $tableName = $modelDefinition['name'];
                } else {
                    $tableName = Helper::codifyName($this->mapping) . '_' . Helper::codifyName($modelName);
                }
                $statement = Mutation\DeleteTable::statement($database, $tableName);
                if ($statement->execute() == false) {
                    throw new DatabaseError('db/build : ' . print_r($statement->errorInfo(), true));
                }
            }
        }
        foreach ($definition as $modelName => $modelDefinition) {
            if (isset($modelDefinition['type']) === true && $modelDefinition['type'] === 'enum') {
                $modelRealName = null;
                if (isset($modelDefinition['name']) === true) {
                    $modelRealName = $modelDefinition['name'];
                } else {
                    $modelRealName = Helper::codifyName($this->mapping) . '_' . Helper::codifyName($modelName);
                }
                $statement = Mutation\DeleteEnum::statement($database, $modelRealName);
                if ($statement->execute() == false) {
                    throw new DatabaseError('db/build : ' . print_r($statement->errorInfo(), true));
                }
            }
        }

        foreach ($definition as $modelName => $modelDefinition) {
            if (isset($modelDefinition['type']) === true && $modelDefinition['type'] === 'enum') {
                $this->buildEnum($modelName, $modelDefinition);
            }
        }
        foreach ($definition as $modelName => $modelDefinition) {
            if (isset($modelDefinition['type']) === false || $modelDefinition['type'] === 'model') {
                $this->buildModel($modelName, $modelDefinition);
            }
        }

        foreach ($definition as $modelName => $modelDefinition) {
            if ((isset($modelDefinition['type']) === false || $modelDefinition['type'] === 'model') && isset($modelDefinition['links']) === true) {
                foreach ($modelDefinition['links'] as $foreignName => $foreignDefinition) {
                    $tableName = null;
                    if (isset($modelDefinition['name']) === true) {
                        $tableName = $modelDefinition['name'];
                    } else {
                        $tableName = Helper::codifyName($this->mapping) . '_' . Helper::codifyName($modelName);
                    }
                    $foreignTableName = null;
                    if (isset($definition[$foreignDefinition['model']]['name']) === true) {
                        $foreignTableName = $definition[$foreignDefinition['model']]['name'];
                    } else {
                        $foreignTableName = Helper::codifyName($this->mapping) . '_' . Helper::codifyName($foreignDefinition['model']);
                    }
                    $statement = Mutation\AddForeignKey::statement($database, $definition, $modelName, $tableName, $foreignName, $foreignTableName);
                    if ($statement->execute() == false) {
                        throw new DatabaseError('db/build : ' . print_r($statement->errorInfo(), true));
                    }
                }
            }
        }
        Migration::ensureTable($mapping);
    }

    /**
     * Build an enum.
     *
     * @param string $enumName enum alias
     * @param array $enumDefinition enum definition from a schema file
     * @throws DatabaseError           if a SQL query fails
     * @throws InvalidSchemaDefinition if an enum has no value
     */
    private function buildEnum(string $enumName, array $enumDefinition)
    {
        $mapping = Mapping::get($this->mapping);
        $definition = $this->getDefinition();
        $database = GlobalDatabase::get($mapping['config']['database']);

        $enumRealName = null;
        if (isset($enumDefinition['name']) === true) {
            $enumRealName = $enumDefinition['name'];
        } else {
            $enumRealName = Helper::codifyName($this->mapping) . '_' . Helper::codifyName($enumName);
        }

        if (isset($enumDefinition['values']) === false) {
            throw new InvalidSchemaDefinition('enum "' . $enumName . '" has no value');
        }

        $values = array();
        foreach ($enumDefinition['values'] as $valueName => $valueAlias) {
            $valueRealName = null;
            if (is_string($valueName) === true) {
                $valueRealName = $valueName;
            } else {
                $valueRealName = Helper::codifyName($enumName) . '_' . Helper::codifyName($valueAlias);
            }
            $values[] = $valueRealName;
        }

        $statement = Mutation\AddEnum::statement($database, $enumRealName, $values);
        if ($statement->execute() == false) {
            throw new DatabaseError('db/build : ' . print_r($statement->errorInfo(), true));
        }

        echo $enumName . ' - Done' . PHP_EOL;
    }

    /**
     * Build a model.
     *
     * @param string $modelName model alias
     * @param array $modelDefinition model definition from a schema file
     * @throws DatabaseError           if a SQL query fails
     * @throws InvalidSchemaDefinition if a model has no field
     */
    private function buildModel(string $modelName, array $modelDefinition)
    {
        $mapping = Mapping::get($this->mapping);
        $definition = $this->getDefinition();
        $database = GlobalDatabase::get($mapping['config']['database']);

        $tableName = null;
        if (isset($modelDefinition['name']) === true) {
            $tableName = $modelDefinition['name'];
        } else {
            $tableName = Helper::codifyName($this->mapping) . '_' . Helper::codifyName($modelName);
        }

        if (isset($modelDefinition['fields']) === false) {
            throw new InvalidSchemaDefinition('model "' . $modelName . '" has no field');
        }
        $statement = Mutation\AddTable::statement($database, $this->getDefinition(), $this->mapping, $tableName, $modelName);
        if ($statement->execute() == false) {
            throw new DatabaseError('db/build : ' . print_r($statement->errorInfo(), true));
        }

        if (isset($modelDefinition['indexes']) === true) {
            foreach ($modelDefinition['indexes'] as $indexName => $indexDefinition) {
                $statement = Mutation\AddIndex::statement($database, $modelName, $tableName, $modelDefinition, $indexName);
                if ($statement->execute() == false) {
                    throw new DatabaseError('db/build : ' . print_r($statement->errorInfo(), true));
                }
            }
        }

        if (isset($modelDefinition['uniques']) === true) {
            foreach ($modelDefinition['uniques'] as $uniqueName => $uniqueDefinition) {
                $statement = Mutation\AddUnique::statement($database, $modelName, $tableName, $modelDefinition, $uniqueName);
                if ($statement->execute() == false) {
                    throw new DatabaseError('db/build : ' . print_r($statement->errorInfo(), true));
                }
            }
        }

        echo $modelName . ' - Done' . PHP_EOL;
    }

    /**
     * Generate SQL entities' representing classes.
     *
     * @see generateEnum
     * @see generateModel
     */
    public function generateModels()
    {
        $definition = $this->getDefinition();
        foreach ($definition as $modelName => $modelDefinition) {
            $type = 'model';
            if (isset($modelDefinition['type']) === true && in_array($modelDefinition['type'], [ 'enum', 'model' ]) === true) {
                $type = $modelDefinition['type'];
            }
            if ($type == 'enum') {
                $this->generateEnum($modelName, $modelDefinition);
            } else {
                $this->generateModel($modelName, $modelDefinition);
            }
        }
    }

    /**
     * Generate classes for a enum.
     *
     * @param string $enumName enum alias
     * @param array $enumDefinition enum definition from a schema file
     * @throws InvalidSchemaDefinition if an enum has no values defined
     * @throws FileNotWritable         if a base file can't be opened with write permission
     * @throws FileNotWritable         if a user file can't be opened with write permission
     */
    private function generateEnum(string $enumName, array $enumDefinition)
    {
        $definition = $this->getDefinition();
        $consts = null;

        $enumRealName = null;
        if (isset($enumDefinition['name']) === true) {
            $enumRealName = $enumDefinition['name'];
        } else {
            $enumRealName = Helper::codifyName($this->mapping) . '_' . Helper::codifyName($enumName);
        }

        if (isset($enumDefinition['values']) === false) {
            throw new InvalidSchemaDefinition('enum "' . $enumName . '" has no value');
        }
        foreach ($enumDefinition['values'] as $valueName => $valueAlias) {
            $valueRealName = null;
            if (is_string($valueName) === true) {
                $valueRealName = $valueName;
            } else {
                $valueRealName = Helper::codifyName($enumName) . '_' . Helper::codifyName($valueAlias);
            }
            $consts .= '    const VALUE_' . strtoupper(Helper::codifyName($valueAlias)) . ' = \'' . $enumRealName . '.' . $valueRealName . "';\n";
        }

        $mapping = Mapping::get($this->mapping);

        $path = rtrim($mapping['config']['models']['path'], DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
        $path .= 'Base' . DIRECTORY_SEPARATOR . ucfirst($enumName) . '.php';
        $file = fopen($path, 'w+');
        if ($file === false) {
            throw new FileNotWritable('can\'t open "' . $path . '" with write permission');
        }
        $path .= 'Base' . DIRECTORY_SEPARATOR . ucfirst($enumName) . '.php';
        $content = "<?php\n\nnamespace " . ltrim(rtrim($mapping['config']['models']['namespace'], '\\'), '\\') . "\\Base;\n\nuse RocknRoot\StrayFw\Database\Provider\Enum;\n";
        $content .= "\nclass " . ucfirst($enumName) . " extends Enum\n{\n";
        $content .= "    public function getDatabaseName() : string\n    {\n        return '" . $mapping['config']['database'] . "';\n    }\n\n";
        $content .= "    public function getTableName() : string\n    {\n        return '" . $enumRealName . "';\n    }\n\n";
        $content .= $consts . "\n}";
        if (fwrite($file, $content) === false) {
            throw new FileNotWritable('can\'t write in "' . $path . '"');
        }
        fclose($file);

        $path = rtrim($mapping['config']['models']['path'], DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
        $path .= ucfirst($enumName) . '.php';
        if (file_exists($path) === false) {
            $file = fopen($path, 'w+');
            if ($file === false) {
                throw new FileNotWritable('can\'t open "' . $path . '" with write permission');
            }
            $content = "<?php\n\nnamespace " . ltrim(rtrim($mapping['config']['models']['namespace'], '\\'), '\\') . ";\n\nuse " . rtrim($mapping['config']['models']['namespace'], '\\') . "\\Base\\" . ucfirst($enumName) . " as BaseEnum;\n\nclass " . ucfirst($enumName) . " extends BaseEnum\n{\n}";
            if (fwrite($file, $content) === false) {
                throw new FileNotWritable('can\'t write in "' . $path . '"');
            }
        }

        echo $enumName . ' - Done' . PHP_EOL;
    }

    /**
     * Generate classes for a model.
     *
     * @param string $modelName model alias
     * @param array $modelDefinition model definition from a schema file
     * @throws InvalidSchemaDefinition if a model has no field
     * @throws InvalidSchemaDefinition if a model is linked to an unknown enum
     * @throws InvalidSchemaDefinition if a model is linked to an unknown model
     * @throws InvalidSchemaDefinition if, while building a link, a model has an unknown needed field
     * @throws FileNotWritable         if a base file can't be opened with write permission
     * @throws FileNotWritable         if a user file can't be opened with write permission
     */
    private function generateModel(string $modelName, array $modelDefinition)
    {
        $definition = $this->getDefinition();
        $primary = array();
        $constructor = '    public function __construct(array $fetch = null)' . "\n    {\n        parent::__construct();\n";
        $constructorDefaults = '        if (is_array($fetch) === true && count($fetch) > 0) {' . PHP_EOL . '            $this->new = false;' . "\n        } else {\n" . '            $fetch = array();' . "\n        }\n";
        $consts = null;
        $properties = null;
        $accessors = null;
        $allFieldsRealNames = "    public function getAllFieldsRealNames() : array\n    {\n        return [ ";
        $allFieldsAliases = "    public function getAllFieldsAliases() : array\n    {\n        return [ ";

        $modelRealName = null;
        if (isset($modelDefinition['name']) === true) {
            $modelRealName = $modelDefinition['name'];
        } else {
            $modelRealName = Helper::codifyName($this->mapping) . '_' . Helper::codifyName($modelName);
        }

        if (isset($modelDefinition['fields']) === false) {
            throw new InvalidSchemaDefinition('model "' . $modelName . '" has no field');
        }
        foreach ($modelDefinition['fields'] as $fieldName => $fieldDefinition) {
            $fieldRealName = null;
            if (isset($fieldDefinition['name']) === true) {
                $fieldRealName = $fieldDefinition['name'];
            } else {
                $fieldRealName = Helper::codifyName($modelName) . '_' . Helper::codifyName($fieldName);
            }

            $properties .= '    protected $field' . ucfirst($fieldName) . ";\n";
            $consts .= '    const FIELD_' . strtoupper(Helper::codifyName($fieldName)) . ' = \'' . $modelRealName . '.' . $fieldRealName . "';\n";

            $constructor .= '        $this->field' . ucfirst($fieldName) . ' = [ \'alias\' => \'' . $fieldName . "', 'value' => null ];\n";
            $constructorDefaults .= '        if (isset($fetch[\'' . $fieldRealName . '\']) === true) {' . "\n            ";
            $constructorDefaults .= '$this->set' . ucfirst($fieldName) . '($fetch[\'' . $fieldRealName . "']);\n        } else {\n            ";
            $constructorDefaults .= '$this->set' . ucfirst($fieldName) . '(';
            if (isset($fieldDefinition['default']) === true) {
                if (is_bool($fieldDefinition['default']) === true) {
                    $constructorDefaults .= ($fieldDefinition['default'] === true) ? 'true' : 'false';
                } else {
                    $constructorDefaults .= '\'' . $fieldDefinition['default'] . '\'';
                }
            } else {
                $constructorDefaults .= 'null';
            }
            $constructorDefaults .= ");\n        }\n";

            if (isset($fieldDefinition['primary']) === true && $fieldDefinition['primary'] === true) {
                $primary[] = $fieldName;
            }

            $accessors .= '    public function get' . ucfirst($fieldName) . "()\n    {\n        ";
            switch ($fieldDefinition['type']) {
                case 'string':
                    $accessors .= 'return stripslashes($this->field' . ucfirst($fieldName) . '[\'value\']);';
                    break;
                case 'char':
                    $accessors .= 'return stripslashes($this->field' . ucfirst($fieldName) . '[\'value\']);';
                    break;
                case 'bool':
                    $accessors .= 'return filter_var($this->field' . ucfirst($fieldName) . '[\'value\'], FILTER_VALIDATE_BOOLEAN);';
                    break;
                case 'json':
                    $accessors .= 'return json_decode($this->field' . ucfirst($fieldName) . '[\'value\'], true);';
                    break;
                default:
                    $accessors .= 'return $this->field' . ucfirst($fieldName) . '[\'value\'];';
                    break;
            }
            $accessors .= "\n    }\n\n";

            $accessors .= '    public function set' . ucfirst($fieldName) . '($value)' . "\n    {\n        ";
            if ($fieldDefinition['type'] == 'bool') {
                $accessors .= '$this->field' . ucfirst($fieldName) . '[\'value\'] = (bool) $value;';
            } elseif ($fieldDefinition['type'] == 'json') {
                $accessors .= 'if (is_string($value) === true) {' . PHP_EOL;
                $accessors .= '            $this->field' . ucfirst($fieldName) . '[\'value\'] = $value;' . PHP_EOL;
                $accessors .= '        } else {' . PHP_EOL;
                $accessors .= '            $this->field' . ucfirst($fieldName) . '[\'value\'] = json_encode($value);' . PHP_EOL;
                $accessors .= '        }' . PHP_EOL;
            } else {
                $accessors .= '$this->field' . ucfirst($fieldName) . '[\'value\'] = $value;';
            }
            $accessors .= PHP_EOL . '        $this->modified[\'' . $fieldName . '\'] = true;';
            $accessors .= "\n        return true;\n    }\n\n";

            $allFieldsRealNames .= '\'' . $modelRealName . '.' . $fieldRealName . '\', ';
            $allFieldsAliases .= '\'' . $fieldName . '\', ';
        }

        if (isset($modelDefinition['links']) === true) {
            foreach ($modelDefinition['links'] as $linkName => $linkDefinition) {
                if (isset($definition[$linkDefinition['model']]) === false) {
                    throw new InvalidSchemaDefinition('unknown model for link "' . $linkName . '" of model "' . $modelName . '"');
                }
                $linkedModel = $definition[$linkDefinition['model']];
                $accessors .= '    public function getLinked' . ucfirst($linkName) . "()\n    {\n        ";
                $accessors .= 'return Models\\' . ucfirst($linkDefinition['model']) . '::fetchEntity([ ';
                $links = array();
                foreach ($linkDefinition['fields'] as $from => $to) {
                    if (isset($modelDefinition['fields'][$from]) === false) {
                        throw new InvalidSchemaDefinition('building link : model "' . $modelName . '" has no field named "' . $from . '"');
                    }
                    if (isset($linkedModel['fields'], $linkedModel['fields'][$to]) === false) {
                        throw new InvalidSchemaDefinition('building link : model "' . $linkDefinition['model'] . '" has no field named "' . $to . '"');
                    }
                    $links[] = '\'' . $to . '\' => $this->get' . ucfirst($from) . '()';
                }
                $accessors .= implode(', ', $links) . " ]);\n    }\n\n";
            }
        }

        $allFieldsRealNames = substr($allFieldsRealNames, 0, -2) . " ];\n    }\n\n";
        $allFieldsAliases = substr($allFieldsAliases, 0, -2) . " ];\n    }\n\n";
        $constructor .= $constructorDefaults;
        $constructor .= PHP_EOL . '        $this->modified = array();' . PHP_EOL;
        $constructor .= "    }\n\n";

        $mapping = Mapping::get($this->mapping);

        $path = rtrim($mapping['config']['models']['path'], DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
        $path .= 'Base' . DIRECTORY_SEPARATOR . ucfirst($modelName) . '.php';
        $file = fopen($path, 'w+');
        if ($file === false) {
            throw new FileNotWritable('can\'t open "' . $path . '" with write permission');
        }
        $content = "<?php\n\nnamespace " . ltrim(rtrim($mapping['config']['models']['namespace'], '\\'), '\\') . "\\Base;\n\nuse RocknRoot\StrayFw\Database\Postgres\Model;\n";
        $content .= "\nclass " . ucfirst($modelName) . " extends Model\n{\n";
        $content .= "    public function getDatabaseName() : string\n    {\n        return '" . $mapping['config']['database'] . "';\n    }\n\n";
        $content .= "    public function getTableName() : string\n    {\n        return '" . $modelRealName . "';\n    }\n\n";
        $content .= PHP_EOL . $consts . PHP_EOL . $properties . PHP_EOL;
        $content .= $constructor . $accessors . $allFieldsRealNames . $allFieldsAliases;
        $content .= "    public function getPrimary() : array\n    {\n        return [ '" . implode('\', \'', $primary) . "' ];\n    }\n";
        $content .= "}";
        if (fwrite($file, $content) === false) {
            throw new FileNotWritable('can\'t write in "' . $path . '"');
        }
        fclose($file);

        $path = rtrim($mapping['config']['models']['path'], DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
        $path .= ucfirst($modelName) . '.php';
        if (file_exists($path) === false) {
            $file = fopen($path, 'w+');
            if ($file === false) {
                throw new FileNotWritable('can\'t open "' . $path . '" with write permission');
            }
            $content = "<?php\n\nnamespace " . ltrim(rtrim($mapping['config']['models']['namespace'], '\\'), '\\') . ";\n\nuse " . ltrim(rtrim($mapping['config']['models']['namespace'], '\\'), '\\') . "\\Base\\" . ucfirst($modelName) . " as BaseModel;\n\nclass " . ucfirst($modelName) . " extends BaseModel\n{\n}";
            if (fwrite($file, $content) === false) {
                throw new FileNotWritable('can\'t write in "' . $path . '"');
            }
        }

        echo $modelName . ' - Done' . PHP_EOL;
    }
}


<?php

namespace RocknRoot\StrayFw\Http;

/**
 * Useful functions for the framework users.
 *
 * @abstract
 *
 * @author Nekith <nekith@errant-works.com>
 */
abstract class Helper
{
    /**
     * Extract domain from raw request.
     *
     * @static
     * @param  RawRequest $rawRequest base raw request
     * @return string     domain
     */
    public static function extractDomain(RawRequest $rawRequest)
    {
        $domain = null;
        if (preg_match("/(?P<domain>[a-z0-9][a-z0-9\-]{1,63}\.[a-z\.]{2,6})$/i", $rawRequest->getHost(), $matches)) {
            $domain = $matches['domain'];
        }

        return $domain;
    }

    /**
     * Get nice URL.
     *
     * @static
     * @param  string $url raw URL
     * @return string nice URL
     */
    public static function niceUrl(string $url)
    {
        $nice = null;
        if (($pos = stripos($url, '.')) !== false) {
            list($subDomain, $url) = explode('.', $url);
            $request = Http::getRequest();
            $nice = $request->getRawRequest()->getScheme() . '://';
            if ($subDomain != null) {
                $nice .= $subDomain . '.';
            }
            $nice .= self::extractDomain($request->getRawRequest());
        }

        return $nice . '/' . ltrim((string) preg_replace('/\/+/', '/', $url), '/');
    }
}


<?php
/*
 * (c) 2018: 975L <contact@975l.com>
 * (c) 2018: Laurent Marquet <laurent.marquet@laposte.net>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace c975L\ToolbarBundle\Twig;

use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use c975L\ConfigBundle\Service\ConfigServiceInterface;
use Twig\Environment;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;

/**
 * Twig extension to provide the xhtml code for available 975L dashboards using `toolbar_dashboards('SIZE[lg|md|sm|xs](default md)')`
 * @author Laurent Marquet <laurent.marquet@laposte.net>
 * @copyright 2018 975L <contact@975l.com>
 */
class ToolbarDashboards extends AbstractExtension
{
    /**
     * Stores ConfigServiceInterface
     * @var ConfigServiceInterface
     */
    private $configService;

    /**
     * Stores ContainerInterface
     * @var ContainerInterface
     */
    private $container;

    /**
     * Stores TokenStorageInterface
     * @var TokenStorageInterface
     */
    private $tokenStorage;

    public function __construct(
        ConfigServiceInterface $configService,
        ContainerInterface $container,
        TokenStorageInterface $tokenStorage
    )
    {
        $this->configService = $configService;
        $this->container = $container;
        $this->tokenStorage = $tokenStorage;
    }

    public function getFunctions()
    {
        return array(
            new TwigFunction(
                'toolbar_dashboards',
                array($this, 'dashboards'),
                array(
                    'needs_environment' => true,
                    'is_safe' => array('html'),
                )
            ),
        );
    }

    /**
     * Returns the xhtml code for the available 975L dashboards
     * @return string
     */
    public function dashboards(Environment $environment, $size)
    {
        //Checks user's rights
        $dashboards = null;
        if ($this->tokenStorage->getToken()->getUser() !== null) {
            //Defines installed dashboards
            $dashboardsAvailable = array('ContactForm', 'Email', 'Events', 'ExceptionChecker', 'GiftVoucher', 'PageEdit', 'Payment', 'PurchaseCredits', 'Site', 'User');
            foreach ($dashboardsAvailable as $dashboardAvailable) {
                //Checks if the bundle is installed
                if (is_dir($this->container->getParameter('kernel.root_dir') . '/../vendor/c975l/' . strtolower($dashboardAvailable) . '-bundle') &&
                    //Checks if roleNeeded for that dashboard is defined
                    $this->configService->hasParameter('c975L' . $dashboardAvailable . '.roleNeeded') &&
                    //Checks if User has good roleNeeded for that dashboard
                    $this->container->get('security.authorization_checker')->isGranted($this->configService->getParameter('c975L' . $dashboardAvailable . '.roleNeeded'))
                )
                {
                    $dashboards[] = strtolower($dashboardAvailable);
                }
            }
        }

        //Defines toolbar
        return $environment->render('@c975LToolbar/dashboards.html.twig', array(
                'dashboards' => $dashboards,
                'size' => $size,
            ));
    }
}


<?php

namespace RocknRoot\StrayFw\Database\Postgres\Mutation;

use RocknRoot\StrayFw\Database\Database;
use RocknRoot\StrayFw\Database\Helper;
use RocknRoot\StrayFw\Database\Postgres\Column;
use RocknRoot\StrayFw\Exception\InvalidSchemaDefinition;
use RocknRoot\StrayFw\Logger;

/**
 * Representation for table addition operations.
 *
 * @author Nekith <nekith@errant-works.com>
 */
class AddTable extends Mutation
{
    /**
     * Prepare and return according PDO statement.
     *
     * @throws InvalidSchemaDefinition if a model has no field
     * @param  Database                $database        database
     * @param  array                   $schema          schema definition
     * @param  string                  $mapping         mapping name
     * @param  string                  $tableName       table real name
     * @param  string                  $modelName       model name
     * @return \PDOStatement           $statement       prepared query
     */
    public static function statement(Database $database, array $schema, $mapping, $tableName, $modelName)
    {
        $tableDefinition = $schema[$modelName];
        if (isset($tableDefinition['fields']) === false) {
            throw new InvalidSchemaDefinition('model "' . $modelName . '" has no field');
        }
        $sql = 'CREATE TABLE ' . $tableName . ' (';
        $primary = array();
        foreach ($tableDefinition['fields'] as $fieldName => $fieldDefinition) {
            $fieldRealName = null;
            if (isset($fieldDefinition['name']) === true) {
                $fieldRealName = $fieldDefinition['name'];
            } else {
                $fieldRealName = Helper::codifyName($modelName) . '_' . Helper::codifyName($fieldName);
            }
            $sql .= Column::generateDefinition($schema, $mapping, $fieldRealName, $fieldDefinition);
            if (isset($fieldDefinition['primary']) === true && $fieldDefinition['primary'] === true) {
                $primary[] = $fieldRealName;
            }
            $sql .= ', ';
        }
        if (count($primary) > 0) {
            $sql .= 'CONSTRAINT pk_' . $tableName . ' PRIMARY KEY (' . implode(', ', $primary) . '), ';
        } else {
            Logger::get()->warning('table "' . $tableName . '" has no primary key');
        }
        $sql = substr($sql, 0, -2) . ')';
        $statement = $database->getMasterLink()->prepare($sql);

        return $statement;
    }
}


<?php declare(strict_types=1);

namespace RPQ\Server\Command;

use Monolog\Logger;
use Monolog\Handler\StreamHandler;

use Redis;
use RPQ\Client;
use RPQ\Server\Command\AbstractCommand;
use RPQ\Server\Process\Dispatcher as ProcessDispatcher;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Yaml\Yaml;
use Symfony\Component\Yaml\Exception\ParseException;

/**
 * Command line interface to RPQ
 * @package RPQ\Queue
 */
final class QueueCommand extends AbstractCommand
{
    /**
     * Configures a new console command
     * @return void
     */
    protected function configure()
    {
        $this->setName('queue')
            ->setHidden(true)
            ->setDescription('Initializes a new queue processor')
            ->setDefinition(new InputDefinition([
                new InputOption('config', 'c', InputOption::VALUE_REQUIRED, 'A YAML configuration file'),
                new InputOption('t', 't', InputOption::VALUE_NONE, 'Test configuration and exit'),
                new InputOption('name', null, InputOption::VALUE_OPTIONAL, 'The queue name to work with. Defaults to `default`.'),
            ]));
    }

    /**
     * Executes the command
     * @param InputInterface $input
     * @param OutputInterface $output
     * @return void
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        if (!parent::execute($input, $output)) {
            return 1;
        }

        // If the self-test was called, immediately return
        if ($input->getOption('t') === true) {
            return 0;
        }

        \file_put_contents($this->config['pid'], \getmypid());

        $this->startProcessQueue();
    }

    /**
     * Starts a new RPQ queue in process mode
     * @return void
     */
    private function startProcessQueue()
    {
        $this->logger->info('Starting queue in process mode');
        $dispatcher = new ProcessDispatcher(
            $this->client,
            $this->logger,
            $this->queue,
            \array_merge($this->queueConfig, ['process' => $this->config['process']]),
            [
                'configFile' => $this->configName
            ]
        );

        $dispatcher->start();
    }

    /**
     * Starts a new RPQ queue in streaming mode
     * @todo: Document + implement
     * @return void
     */
    private function startStreamQueue()
    {
    }
}


<?php
use SYSLang\SYSLang;

class SYSLangCompilator extends SYSLang
{
    /**
     * Compile le ou les langues spécifiées au format xx-XX
     * @param string $packages Pack de langue à compiler
     * @return bool
     */
	public function compile($packages=null){
                /** #2. Lister les packages à compiler **/
                //$packages_to_compile = Array();

                /** Si $package n'est pas null, alors lister les package de destination **/
                if($packages !== null){
                    /** Sauvegarder les packages selon s'ils sont enregistré **/
                    $packages = func_get_args();

                    foreach($packages as $key => $value){
                        if(array_key_exists($value, $avail_languages)){
                            $packages_to_compile[$value] = $avail_languages[$value];
                        }
                    }
                }
                else {
                    $packages_to_compile = $avail_languages;
                }

                // #3.3. Vérifier qu'un rapport MD5 pour le pack de référence existe
        /** #4. Lancer la compilation **/
        // RAZ pour execution multiple de compile par la meme instance
        $this->_ref_packages_keys = Array();
        $this->_ini_sxe_resources = Array();
	}

    /**
     * Importe les fichiers de langue INI dans l'environnement de fonctionnement SYSLang
     * @param bool $finalise Indique si on "commit", donc sauvegarde et met à jour le référentiel MD5
     * @return bool
     */
}
?>


<?php

namespace RocknRoot\StrayFw\Locale;

use RocknRoot\StrayFw\Config;
use RocknRoot\StrayFw\Exception\BadUse;
use RocknRoot\StrayFw\Exception\InvalidDirectory;
use RocknRoot\StrayFw\Http\Helper as HttpHelper;
use RocknRoot\StrayFw\Http\RawRequest;
use RocknRoot\StrayFw\Http\Session;
use RocknRoot\StrayFw\Logger;

/**
 * Internationalization and localization base class.
 *
 * @abstract
 *
 * @author Nekith <nekith@errant-works.com>
 */
abstract class Locale
{
    /**
     * True if class has already been initialized.
     *
     * @static
     * @var bool
     */
    private static $isInit = false;

    /**
     * Current language. Should be IETF language tag compliant.
     *
     * @see http://en.wikipedia.org/wiki/IETF_language_tag
     *
     * @static
     * @var string
     */
    protected static $currentLanguage = null;

    /**
     * Loaded translations.
     *
     * @static
     * @var mixed[]
     */
    protected static $translations = array();

    /**
     * Initialize properties, detect current language and load translations.
     *
     * @static
     * @throws BadUse     if locale.default isn\'t defined in settings
     * @param  RawRequest $request base raw request if applied
     */
    public static function init(RawRequest $request = null)
    {
        if (self::$isInit === false) {
            self::$translations = array();
            $settings = Config::getSettings();
            if (isset($settings['locale']) === false || isset($settings['locale']['default']) === false) {
                throw new BadUse('locale.default isn\'t defined in settings');
            }
            self::$currentLanguage = $settings['locale']['default'];
            if ($request != null) {
                if (Session::has('_stray_language') === true) {
                    self::$currentLanguage = Session::get('_stray_language');
                } else {
                    $domain = HttpHelper::extractDomain($request);
                    if (isset($settings['locale']['hosts']) === true && isset($settings['locale']['hosts'][$domain]) === true) {
                        self::$currentLanguage = $settings['locale']['hosts'][$domain];
                    }
                    Session::set('_stray_language', self::$currentLanguage);
                }
            }
            self::$isInit = true;
        }
    }

    /**
     * Load translations from directory according to current language.
     *
     * @static
     * @throws InvalidDirectory if directory can't be identified
     * @param  string           $baseDir    application directory path
     * @param  string           $localesDir translations directory path
     * @param  string           $prefix     prefix for all translations from this directory
     */
    public static function registerTranslations($baseDir, $localesDir, $prefix = null)
    {
        if (self::$isInit === true) {
            $dir = $baseDir . DIRECTORY_SEPARATOR . $localesDir;
            if (is_dir($dir) === false) {
                throw new InvalidDirectory('directory "' . $dir . '" can\'t be identified');
            }
            $language = self::$currentLanguage;
            if (($pos = strpos($language, '-')) !== false) {
                $pos = (int) $pos; // re: https://github.com/phpstan/phpstan/issues/647
                $language = substr($language, 0, $pos);
            }
            if (($pos = strpos($language, '_')) !== false) {
                $pos = (int) $pos; // re: https://github.com/phpstan/phpstan/issues/647
                $language = substr($language, 0, $pos);
            }
            if (is_readable($dir . DIRECTORY_SEPARATOR . $language . '.yml') === true) {
                $newOnes = Config::get($dir . DIRECTORY_SEPARATOR . $language . '.yml');
                if (is_array($newOnes) === true) {
                    if ($prefix != null) {
                        $newOnes = array($prefix => $newOnes);
                    }
                    self::$translations = array_merge(self::$translations, $newOnes);
                }
            } else {
                Logger::get()->notice('can\'t find language "' . $language . '" in directory "' . $dir . '"');
            }
        }
    }

    /**
     * Get a translation from loaded files.
     *
     * @static
     * @throws BadUse if locale isn't initialized
     */
    public static function translate($key, array $args = array())
    {
        if (self::$isInit === false) {
            throw new BadUse('locale doesn\'t seem to have been initialized');
        }
        $oldKey = $key;
        $section = self::$translations;
        while (isset($section[$key]) === false && ($pos = strpos($key, '.')) !== false) {
            if (! is_int($pos)) { // re: https://github.com/phpstan/phpstan/issues/647
                break;
            }
            $subSection = substr($key, 0, $pos);
            if (isset($section[$subSection]) === false) {
                break;
            }
            $section = $section[$subSection];
            $key = substr($key, $pos + 1);
        }
        if (isset($section[$key]) === false) {
            Logger::get()->error('can\'t find translation for key "' . $oldKey . '"');

            return '(null)';
        }

        return $section[$key];
    }

    /**
     * Set current language. Should be IETF language tag compliant.
     *
     * @static
     * @param string $language
     */
    public static function setCurrentLanguage($language)
    {
        self::$currentLanguage = $language;
        Session::set('_stray_language', self::$currentLanguage);
        setlocale(LC_ALL, $language);
    }

    /**
     * Get current language. Should be IETF language tag compliant.
     *
     * @static
     * @return string
     */
    public static function getCurrentLanguage()
    {
        return self::$currentLanguage;
    }
}


<?php

declare(strict_types=1);

/*
 * This file is part of the BkstgNoticeBoardBundle package.
 * (c) Luke Bainbridge <http://www.lukebainbridge.ca/>
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Bkstg\NoticeBoardBundle\Timeline\EventSubscriber;

use Bkstg\CoreBundle\Event\EntityPublishedEvent;
use Bkstg\NoticeBoardBundle\Entity\Post;
use Spy\Timeline\Driver\ActionManagerInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;

class PostTimelineSubscriber implements EventSubscriberInterface
{
    private $action_manager;
    private $user_provider;

    /**
     * Create a new post notification listener.
     *
     * @param ActionManagerInterface $action_manager The action manager service.
     * @param UserProviderInterface  $user_provider  The user provider service.
     */
    public function __construct(
        ActionManagerInterface $action_manager,
        UserProviderInterface $user_provider
    ) {
        $this->action_manager = $action_manager;
        $this->user_provider = $user_provider;
    }

    /**
     * Return the events this subscriber listens for.
     *
     * @return array The subscribed events.
     */
    public static function getSubscribedEvents(): array
    {
        return [
            EntityPublishedEvent::NAME => [
                ['createPostTimelineEntry', 0],
            ],
        ];
    }

    /**
     * Create the timeline entry for the new post.
     *
     * @param EntityPublishedEvent $event The entity published event.
     *
     * @return void
     */
    public function createPostTimelineEntry(EntityPublishedEvent $event): void
    {
        // Only act on post objects.
        $post = $event->getObject();
        if (!$post instanceof Post) {
            return;
        }

        // Get the author for the post.
        $author = $this->user_provider->loadUserByUsername($post->getAuthor());

        // Create components for this action.
        $post_component = $this->action_manager->findOrCreateComponent($post);
        $author_component = $this->action_manager->findOrCreateComponent($author);

        // Add timeline entries for each group.
        foreach ($post->getGroups() as $group) {
            // Create the group component.
            $group_component = $this->action_manager->findOrCreateComponent($group);

            // Either a new post was created or a reply was created.
            if (null === $post->getParent()) {
                $verb = 'post';
            } else {
                $verb = 'reply';
            }

            // Create the action and link it.
            $action = $this->action_manager->create($author_component, $verb, [
                'directComplement' => $post_component,
                'indirectComplement' => $group_component,
            ]);

            // Update the action.
            $this->action_manager->updateAction($action);
        }
    }
}


<?php

namespace RocknRoot\StrayFw\Database;

use RocknRoot\StrayFw\Config;
use RocknRoot\StrayFw\Exception\BadUse;
use RocknRoot\StrayFw\Exception\MappingNotFound;
use RocknRoot\StrayFw\Logger;

/**
 * Mapping representation class.
 *
 * @author Nekith <nekith@errant-works.com>
 */
class Mapping
{
    /**
     * Registered mappings.
     *
     * @static
     * @var array[]
     */
    protected static $mappings = array();

    /**
     * Register a new mapping.
     *
     * @static
     * @param array $config mapping configuration
     */
    public static function registerMapping(array $config)
    {
        self::validateConfig($config);
        if (isset(self::$mappings[$config['name']]) === false) {
            self::$mappings[$config['name']] = array(
                'config' => $config
            );
            Database::registerDatabase($config['database']);
        } else {
            Logger::get()->warning('mapping with name "' . $config['name'] . '" was already set');
        }
    }

    /**
     * Get all mappings.
     *
     * @return array[] mappings
     */
    public static function getMappings() : array
    {
        return self::$mappings;
    }

    /**
     * Get the mapping data.
     *
     * @throws MappingNotFound if there's no registered mapping for specified database
     * @param  string          $name mapping name
     * @return array           mapping data
     */
    public static function get(string $name) : array
    {
        if (isset(self::$mappings[$name]) === false) {
            throw new MappingNotFound('there\'s no registered mapping with name "' . $name . '"');
        }

        return self::$mappings[$name];
    }

    /**
     * Check the validation of mapping configuration.
     *
     * @throws BadUse if there's no name in mapping configuration
     * @throws BadUse if there's no schema in mapping configuration
     * @throws BadUse if there's no provider in mapping configuration
     * @throws BadUse if there's no models in mapping configuration
     * @throws BadUse if there's no models.path in mapping configuration
     * @throws BadUse if there's no models.namespace in mapping configuration
     * @param  array  $config mapping configuration
     */
    private static function validateConfig(array $config)
    {
        if (isset($config['name']) === false) {
            throw new BadUse('there\'s no name in mapping configuration');
        }
        if (isset($config['schema']) === false) {
            throw new BadUse('there\'s no schema in mapping configuration');
        }
        if (isset($config['provider']) === false) {
            throw new BadUse('there\'s no provider in mapping configuration');
        }
        if (isset($config['models']) === false) {
            throw new BadUse('there\'s no models in mapping configuration');
        }
        if (isset($config['models']['path']) === false) {
            throw new BadUse('there\'s no models.path in mapping configuration');
        }
        if (isset($config['models']['namespace']) === false) {
            throw new BadUse('there\'s no models.namespace in mapping configuration');
        }
    }
}


<?php

declare(strict_types=1);

/*
 * This file is part of the BkstgNoticeBoardBundle package.
 * (c) Luke Bainbridge <http://www.lukebainbridge.ca/>
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Bkstg\NoticeBoardBundle\Controller;

use Bkstg\CoreBundle\Controller\Controller;
use Bkstg\CoreBundle\Entity\Production;
use Bkstg\NoticeBoardBundle\Entity\Post;
use Knp\Component\Pager\PaginatorInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;

class BoardController extends Controller
{
    /**
     * Show the notice board.
     *
     * @param string                        $production_slug The production slug.
     * @param AuthorizationCheckerInterface $auth            The authorization checker service.
     * @param PaginatorInterface            $paginator       The paginator service.
     * @param Request                       $request         The incoming request.
     *
     * @throws NotFoundHttpException When the production is not found.
     * @throws AccessDeniedException When the user is not a member of the production.
     *
     * @return Response
     */
    public function showAction(
        string $production_slug,
        AuthorizationCheckerInterface $auth,
        PaginatorInterface $paginator,
        Request $request
    ): Response {
        // Lookup the production by production_slug.
        $production_repo = $this->em->getRepository(Production::class);
        if (null === $production = $production_repo->findOneBy(['slug' => $production_slug])) {
            throw new NotFoundHttpException();
        }

        // Check permissions for this action.
        if (!$auth->isGranted('GROUP_ROLE_USER', $production)) {
            throw new AccessDeniedException();
        }

        // Get notice board posts.
        $query = $this->em->getRepository(Post::class)->getAllActiveQuery($production);

        // Return response.
        $posts = $paginator->paginate($query, $request->query->getInt('page', 1));

        return new Response($this->templating->render('@BkstgNoticeBoard/Board/show.html.twig', [
            'production' => $production,
            'posts' => $posts,
        ]));
    }

    /**
     * Show archive for the notice board.
     *
     * @param string                        $production_slug The production slug.
     * @param AuthorizationCheckerInterface $auth            The authorization checker service.
     * @param PaginatorInterface            $paginator       The paginator service.
     * @param Request                       $request         The incoming request.
     *
     * @throws NotFoundHttpException When the production is not found.
     * @throws AccessDeniedException When the user is not an editor of the production.
     *
     * @return Response
     */
    public function archiveAction(
        string $production_slug,
        AuthorizationCheckerInterface $auth,
        PaginatorInterface $paginator,
        Request $request
    ): Response {
        // Lookup the production by production_slug.
        $production_repo = $this->em->getRepository(Production::class);
        if (null === $production = $production_repo->findOneBy(['slug' => $production_slug])) {
            throw new NotFoundHttpException();
        }

        // Check permissions for this action.
        if (!$auth->isGranted('GROUP_ROLE_EDITOR', $production)) {
            throw new AccessDeniedException();
        }

        // Get notice board posts.
        $query = $this->em->getRepository(Post::class)->getAllInactiveQuery($production);

        // Return response.
        $posts = $paginator->paginate($query, $request->query->getInt('page', 1));

        return new Response($this->templating->render('@BkstgNoticeBoard/Board/archive.html.twig', [
            'production' => $production,
            'posts' => $posts,
        ]));
    }
}


<?php

namespace RocknRoot\StrayFw\Database\Postgres\Mutation;

use RocknRoot\StrayFw\Database\Database;

/**
 * Representation for enum type deletion operations.
 *
 * @author Nekith <nekith@errant-works.com>
 */
class DeleteEnum extends Mutation
{
    /**
     * Prepare and return according PDO statement.
     *
     * @param  Database     $database database
     * @param  string       $type     type name
     * @return \PDOStatement $statement prepared query
     */
    public static function statement(Database $database, $type)
    {
        $statement = $database->getMasterLink()->prepare('DROP TYPE IF EXISTS ' . $type);

        return $statement;
    }
}


<?php declare(strict_types=1);

namespace RPQ\Server\Command;

use Exception;
use Redis;
use RPQ\Client;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Yaml\Yaml;
use Symfony\Component\Yaml\Exception\ParseException;

abstract class AbstractCommand extends Command
{
    /**
     * @var Redis $redis
     */
    protected $redis;

    /**
     * @var array $config
     */
    protected $config;

    /**
     * @var Client $client
     */
    protected $client;

    /**
     * @var MonoLog $logger
     */
    protected $logger;

    /**
     * @var string $configName
     */
    protected $configName;

    /**
     * @var Queue $queue
     */
    protected $queue;

    /**
     * @var string $queueConfig
     */
    protected $queueConfig;

    /**
     * Executes the command
     * @param InputInterface $input
     * @param OutputInterface $output
     * @return void
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $this->configName = $input->getOption('config');

        // Verify the configuration file provided is valid
        if ($this->configName === null || !\file_exists($this->configName)) {
            $output->writeln("Please specify a valid configuration YAML file");
            return false;
        }

        try {
            $this->config = Yaml::parse(\file_get_contents($this->configName));
        } catch (ParseException $e) {
            $output->writeln("Unable to parse the YAML string: %s", $e->getMessage());
            return false;
        }

        $this->redis = new Redis;
        $this->redis->connect($this->config['redis']['host'], $this->config['redis']['port']);
        $this->redis->echo('Hello RPQ');
        $this->client = new Client($this->redis, $this->config['redis']['namespace']);
        $this->queue = $this->client->getQueue($input->getOption('name') ?? 'default');

        defined('LOGGER_APP_NAME') or define('LOGGER_APP_NAME', 'rpq.' . \bin2hex(random_bytes(4)));

        // If a default logger configuration isn't set, pipe data to stdout
        if (!isset($this->config['log']['logger']) ||
            !\file_exists($this->config['log']['logger'])
        ) {
            $this->logger = new Logger(LOGGER_APP_NAME);
            $handler = new StreamHandler('php://stdout', Logger::DEBUG);
            $this->logger->pushHandler($handler, Logger::DEBUG);
        } else {
            // Otherwise use the preconfigured logger
            $this->logger = require $this->config['log']['logger'];
        }

        $this->queueConfig = $this->config['queue']['default'];
        if (isset($this->config['queue'][$this->queue->getName()])) {
            $this->queueConfig = $this->config['queue'][$this->queue->getName()];
        }

        return true;
    }
}


<?php

namespace RocknRoot\StrayFw\Database\Postgres\Mutation;

use RocknRoot\StrayFw\Database\Database;
use RocknRoot\StrayFw\Database\Helper;

/**
 * Representation for unique constraint addition operations.
 *
 * @author Nekith <nekith@errant-works.com>
 */
class AddUnique extends Mutation
{
    /**
     * Prepare and return according PDO statement.
     *
     * @param  Database     $database         database
     * @param  string       $modelName        model name
     * @param  string       $tableName        table real name
     * @param  array        $tableDefinition  table definition
     * @param  string       $uniqueName       unique constraint name
     * @return \PDOStatement $statement prepared query
     */
    public static function statement(Database $database, $modelName, $tableName, array $tableDefinition, $uniqueName)
    {
        $uniqueDefinition = $tableDefinition['uniques'][$uniqueName];
        $fields = array();
        foreach ($uniqueDefinition as $field) {
            if (isset($tableDefinition['fields'][$field]['name']) === true) {
                $fields[] = $tableDefinition['fields'][$field]['name'];
            } else {
                $fields[] = Helper::codifyName($modelName) . '_' . Helper::codifyName($field);
            }
        }
        $statement = $database->getMasterLink()->prepare('ALTER TABLE ' . $tableName . ' ADD CONSTRAINT ' . $uniqueName . ' UNIQUE (' . implode(', ', $fields) . ')');

        return $statement;
    }
}


<?php

namespace Railken\Amethyst\Providers;

use Illuminate\Support\Facades\Config;
use Railken\Amethyst\Common\CommonServiceProvider;

class LegalEntityServiceProvider extends CommonServiceProvider
{
    /**
     * Register the service provider.
     */
    public function register()
    {
        parent::register();

        $this->app->register(\Railken\Amethyst\Providers\AddressServiceProvider::class);
        $this->app->register(\Railken\Amethyst\Providers\TaxonomyServiceProvider::class);

        Config::set('amethyst.taxonomy.data.taxonomy.seeds', array_merge(
            Config::get('amethyst.taxonomy.data.taxonomy.seeds'),
            Config::get('amethyst.legal-entity.taxonomies')
        ));
    }
}


<?php
/**
 * File :: SYSLang.php
 *
 * Interface d'utilisation de SYSLang pour PHP.
 *
 * @author    Nicolas DUPRE
 * @release   18/10/2017
 * @version   2.0.0-beta1
 * @package   Index
 *
 * @TODO : Faire les méthodes pour ajouter et supprimer les targets possible.
 */

namespace SYSLang;

class SYSLang extends Core
{
    /**
     * @var string $useLanguage Langue définie qui sera utilisée par le système.
     */
    protected $useLanguage = null;

    /**
     * @var array $target Liste des attributs XML définissant une cible de sortie avec pour information le nom et
     * la fonction de traitement admettant trois arguments : $key et $val qui seront rattaché à $arr
     */
    protected $targets = [
        'SST' => [
            'name' => 'SERVER',
            'callback' => 'self::sstCallback'
        ],
        'CST' => [
            'name' => 'CLIENT',
            'callback' => 'self::cstCallback'
        ]
    ];

    /**
     * SYSLang constructor.
     * @param string $language          Langue à utiliser.
     * @param string $workingDirectory  Dossier d'installation.
     */
    function __construct($workingDirectory = ".", $language = null)
    {
        parent::__construct($workingDirectory);

        /**
         * SI    $language est valide et définie, vérifier sa disponibilité.
         * SINON Définir la langue selon le système utilisateur (ou par défaut si disponible).
         */
        if (!is_null($language) && parent::checkCode($language) && $this->isRegistered($language)) {
            $this->setLanguage($language);
        } else {
            $this->getUserLanguages();
        }
    }

    /**
     * SYSLang destructor.
     */
    function __destruct()
    {
        parent::__destruct();
    }

    /**
     * Fonction de rappel pour le formatage des données pour la cible SST pour le moteur Template.
     *
     * @param string $key Valeur de l'attribut KEY.
     * @param string $val Text qui lui associé.
     * @param array  $arr Le tableau qui recevra les données.
     *
     * @return bool
     */
    private static function sstCallback($key, $val, &$arr)
    {
        $arr[$key] = $val;
        return true;
    }

    /**
     * Fonction de rappel pour le formatage des données pour la cible CST pour le moteur Template.
     *
     * @param string $key Valeur de l'attribut KEY.
     * @param string $val Text qui lui associé.
     * @param array  $arr Le tableau qui recevra les données.
     *
     * @return bool
     */
    private static function cstCallback($key, $val, &$arr)
    {
        $arr[] = [
            "VAR_KEY" => $key,
            "VAR_VALUE" => $val
        ];
        return true;
    }

    /**
     * Extrait les textes associés à leur clé et les ranges dans le tableau fournir en argument.
     *
     * @param string $source Emplacement vers la/les source(s) xml.
     * @param array  $output Tableau receuillant les ressources, rangé par target.
     */
    protected function extract($source, array &$output)
    {
        $dirname = dirname($source);

        // Traitement spécifique aux dossiers.
        if (is_dir($source)) {
            $dh = opendir($source);

            while($file = readdir($dh)){
                if (preg_match('/^\.{1,2}$/', $file)) continue;

                $this->extract($source . '/' . $file, $output);
            }

            closedir($dh);
        }
        // Traitement spécifique aux fichiers.
        if (is_file($source)) {
            try {
                $sxeResource = new \SimpleXMLElement(file_get_contents($source));

                // Parcourir la ressource XML
                for ($i = 0; $i < count($sxeResource->resource); $i++) {
                    // Traitement des elements commun :
                    $resource = $sxeResource->resource[$i];

                    $key = strval($resource->attributes()->KEY);
                    $value = strval($resource);

                    // Traitements des différentes sorties possibles
                    foreach ($this->targets as $attribut => $instructions) {
                        if(is_null($resource->attributes()->$attribut)) continue;
                        if(strtolower(strval($resource->attributes()->$attribut)) === 'false') continue;

                        $targetName = $instructions['name'];
                        $callback = $instructions['callback'];

                        if(!isset($output[$targetName])) $output[$targetName] = [];

                        call_user_func_array($callback, [$key, $value, &$output[$targetName]]);
                    }
                }
            } catch (\Exception $e) {
                // Ne pas emettre d'erreur
            }
        }
    }

    /**
     * Renvoie la langue utilisée.
     *
     * @return string
     */
    public function getLanguage()
    {
        return $this->useLanguage;
    }

    /**
     * Récupère l'ensemble des textes associé à leur clé, ordonné en fonction de leur cible à l'aide des attributs.
     *
     * @param null|string $langFile Autant d'emplacement (Fichier ou Dossier) dont il faut traiter.
     *
     * @throws \Exception
     *
     * @return array
     */
    public function getTexts($langFile = null)
    {
        $sources = [];
        $outputs = [];

        // Si aucun argument, alors on extrait l'intégralité du dossier.
        if (is_null($langFile)) {
            $sources[] = '.';
        } else {
            $sources = func_get_args();
        }

        // Parcourir les sources
        foreach ($sources as $index => $source) {
            $sourceFullPath = $this->workingDirectory . '/' . $this->useLanguage . '/' . $source;
            if (!file_exists($sourceFullPath)) throw new \Exception(
                sprintf("The following source '%s' not found in '%s'",
                    $source,
                    $this->workingDirectory . '/' . $this->useLanguage
                )
            );
            $this->extract($sourceFullPath, $outputs);
        }

        return $outputs;
    }

    /**
     * Traite les paramètres de langue de l'utilisateur pour définir la langue à utilisée.
     * Si les paramètres ne sont pas compatible, la langue par défaut est utilisée.
     */
    public function getUserLanguages()
    {
        $acceptLanguage = (isset($_SERVER['HTTP_ACCEPT_LANGUAGE']))
            ? explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE'])
            : [];
        $userLanguages = [];

        /**
         * Accept-Language: <language>
         * Accept-Language: <locale>
         * Accept-Language: *
         *
         * // Multiple types, weighted with the quality value syntax:
         * Accept-Language: fr-FR, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5
         *
         * CHROME : Array ( [0] => fr-FR [1] => fr;q=0.8    [2] => en-US;q=0.6 [3] => en;q=0.4 )
         * FIREFO : Array ( [0] => fr    [1] => fr-FR;q=0.8 [2] => en-US;q=0.5 [3] => en;q=0.3 )
         * OPERA  : Array ( [0] => fr-FR [1] => fr;q=0.8    [2] => en-US;q=0.6 [3] => en;q=0.4 )
         * SAFARI : Array ( [0] => fr-FR )
         * IE     : Array ( [0] => fr-FR )
         */
        foreach ($acceptLanguage as $key => $language) {
            $language = trim($language);

            if(!preg_match('#^[a-z]{2}#', $language)) continue;

            preg_match_all('#^[a-z]{2}(-[A-Z]{2})?#', $language, $matches);

            $code = $matches[0][0];
            if(preg_match("/^[a-z]{2}$/", $code)) $code .= '-' . strtoupper($code);

            if (!in_array($code, $userLanguages)) $userLanguages[] = $code;
        }

        /**
         * Si aucune langue n'à pu être trouvée, alors utilisé la langue définie dans le fichier de config.
         */
        if (count($userLanguages) === 0) {
            $this->useLanguage = $this->defaultLanguage;
        } else {
            $useDefault = true;

            foreach ($userLanguages as $key => $language) {
                if ($this->isRegistered($language)) {
                    $this->setLanguage($language);
                    $useDefault = false;
                    break;
                }
            }

            if ($useDefault) $this->useLanguage = $this->defaultLanguage;
        }
    }

    /**
     * Définie la langue à utilisée pour l'extraction des textes.
     *
     * @param null|string $lang Code de lange à utiliser.
     *
     * @return bool
     */
    public function setLanguage($lang = null)
    {
        if (is_null($lang) || !$this->isRegistered($lang)) {
            $this->getUserLanguages();
        } else {
            $this->useLanguage = $lang;
            //setcookie('SYSLang_LANG', $lang, time()+365*24*60*60, '/');
        }

        return true;
    }

}


<?php
declare(strict_types=1);
declare(ticks=1);

require './vendor/autoload.php';

use Redis;
use RPQ\Server\AbstractJob;
use RPQ\Client\Client;
use Swift_Mailer;
use Swift_Message;
use Swift_SmtpTransport;

final class BatchEmailJob extends AbstractJob
{
    /**
     * Indicate if we're to shut down
     * @var boolean $shutdown
     */
    private $shutdown = false;

    /**
     * An array of all of our messages
     * @var array $messages
     */
    private $messages;

    /**
     * The initial arguements sent to `perform`
     * @var array $args
     */
    private $args = [];

    /**
     * Sends batches of emails in a loop.
     *
     * `$args` is an array of details pushed into Redis. In this example `$args` has the following
     * structure
     * @param array $args = [
     *      'smtp' => [
     *          'host' => '127.0.0.1',
     *          'port' => 25
     *          'username' => 'smtp_username',
     *          'password' => 'smtp_password'
     *      ],
     *      // This is an array of messages, where each message is it's own array
     *      'messages' => [
     *          [
     *              'subject' => 'Hello Doctor!',
     *              'message' => 'What are you doing for Christmas?',
     *              'from' => [
     *              'email' => 'clara.oswald@tardis.io',
     *                 'name' => 'Clara Oswald
     *              ],
     *              'to' => [
     *                 'email' => 'doctor@tardis.io',
     *                 'name' => 'Doctor'
     *              ]
     *          ],
     *          [ ... ] // More messages
     *      ]
     * ];
     * @return int
     */
    public function perform(array $args = []): int
    {
        // Store our args
        $this->args = $args;
        unset($this->args['messages']);

        // Create a new transport
        $transport = new Swift_SmtpTransport(
            $args['smtp']['host'],
            $args['smtp']['port']
        );
        $transport->setUsername($args['smtp']['username']);
        $transport->setPassword($args['smtp']['password']);

        // Make a copy of all of our messages, and interate over each message
        $this->messages = $args['messages'];
        foreach ($messages as &$message) {
            // If shutdown is called, abort what we are doing.
            if ($shutdown) {
                break;
            }

            // Create a message
            $mailer = new Swift_Mailer($transport);
            $m = (new Swift_Message($message['subject']))
                ->setFrom([$message['from']['email'] => $message['from']['email']])
                ->setTo([$message['to']['email'] => $message['to']['name']])
                ->setBody($message['message']);

            // Send the message, and indicate if it was sent
            $message['sent'] = ($mailer->send($m) === 1);
        }

        return 0;
    }

    /**
     * Register a shutdown handler
     * @return bool
     */
    protected function shutdown()
    {
        // Indicate to our main loop that we should stop processing additonal messages
        $this->shutdown = true;

        // Get a list of all the messages that have not yet been handled
        $this->args['messages'] = array_filter($this->messages, function($message) {
            if (!isset($message['sent']) || $message['sent'] === false) {
                return $message;
            }
        });

        $redis = new Redis;
        $client = new Client($redis);

        // Push the unfinished jobs back onto the priority queue with a priority of 100
        // So they get processed as soon as possible.
        $client->push(static::class, $this->args, 1, 100);

        // Indicate that we've handled SIGTERM, and are ready to shut down
        return true;
    }
}


<?php

namespace RocknRoot\StrayFw\Database\Postgres\Mutation;

use RocknRoot\StrayFw\Database\Database;
use RocknRoot\StrayFw\Database\Helper;

/**
 * Representation for foreign key addition operations.
 *
 * @author Nekith <nekith@errant-works.com>
 */
class AddForeignKey extends Mutation
{
    /**
     * Prepare and return according PDO statement.
     *
     * @param  Database     $database         database
     * @param  array        $definition       schema definition
     * @param  string       $modelName        model name
     * @param  string       $tableName        table real name
     * @param  string       $foreignName      foreign key name
     * @param  string       $foreignTableName foreign table real name
     * @return \PDOStatement $statement prepared query
     */
    public static function statement(Database $database, array $definition, $modelName, $tableName, $foreignName, $foreignTableName)
    {
        $tableDefinition = $definition[$modelName];
        $foreignDefinition = $tableDefinition['links'][$foreignName];
        $foreignTableDefinition = $definition[$foreignDefinition['model']];
        $from = array();
        $to = array();
        foreach ($foreignDefinition['fields'] as $field => $linked) {
            if (isset($tableDefinition['fields'][$field]['name']) === true) {
                $from[] = $tableDefinition['fields'][$field]['name'];
            } else {
                $from[] = Helper::codifyName($modelName) . '_' . Helper::codifyName($field);
            }
            if (isset($foreignTableDefinition['fields'][$linked]['name']) === true) {
                $to[] = $foreignTableDefinition['fields'][$linked]['name'];
            } else {
                $to[] = Helper::codifyName($foreignDefinition['model']) . '_' . Helper::codifyName($linked);
            }
        }
        $sql = 'ALTER TABLE ' . $tableName . ' ADD CONSTRAINT fk_' . $foreignName . ' FOREIGN KEY (' . implode(', ', $from) . ') REFERENCES ' . $foreignTableName . '(' . implode(', ', $to) . ')';
        if (isset($foreignDefinition['update']) === true) {
            $sql .= ' ON UPDATE ' . strtoupper($foreignDefinition['update']);
        }
        if (isset($foreignDefinition['delete']) === true) {
            $sql .= ' ON DELETE ' . strtoupper($foreignDefinition['delete']);
        }
        $statement = $database->getMasterLink()->prepare($sql);

        return $statement;
    }
}


<?php

namespace RocknRoot\StrayFw\Console;

/**
 * Basic actions for console.
 *
 * @author Nekith <nekith@errant-works.com>
 */
class Controller
{
    /**
     * Basic help action listing available all console actions.
     *
     * @param Request $request current request
     */
    public function help(Request $request)
    {
        $routes = Console::getRoutes();
        echo 'strayFw console help screen' . PHP_EOL . 'Available actions :' . PHP_EOL . PHP_EOL;
        $namespace = null;
        foreach ($routes as $route) {
            if ($namespace != $route['namespace']) {
                $namespace = $route['namespace'];
                echo $namespace . PHP_EOL . PHP_EOL;
            }
            echo '    ' . $route['usage'] . PHP_EOL;
            if (isset($route['help']) != null) {
                echo '        ' . $route['help'];
            }
            echo PHP_EOL . PHP_EOL;
        }
    }
}


<?php declare(strict_types=1);

namespace RPQ\Server\Command;

use Exception;
use Redis;
use RPQ\Client;
use RPQ\Client\Exception\JobNotFoundException;
use RPQ\Server\Command\AbstractCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Yaml\Yaml;
use Symfony\Component\Yaml\Exception\ParseException;

final class ProcessWorkerCommand extends AbstractCommand
{
    /**
     * Configures a new console command
     * @return void
     */
    protected function configure()
    {
        $this->setName('worker/process')
             ->setHidden(true)
             ->setDescription('Runs a given worker')
             ->setDefinition(new InputDefinition([
                new InputOption('config', 'c', InputOption::VALUE_REQUIRED, 'A YAML configuration file'),
                new InputOption('jobId', null, InputOption::VALUE_REQUIRED, 'A Job UUID'),
                new InputOption('name', null, InputOption::VALUE_REQUIRED, 'The queue name to work with. Defaults to `default`.'),
             ]));
    }

    /**
     * Executes the command
     * @param InputInterface $input
     * @param OutputInterface $output
     * @return void
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        if (!parent::execute($input, $output)) {
            return 1;
        }

        try {
            $job = $this->queue->getJob($input->getOption('jobId'));
            $class = $job->getWorkerClass();
            if (!\class_exists($class)) {
                throw new Exception("Unable to find worker class {$class}");
            }

            if (!\is_subclass_of($class, 'RPQ\Server\AbstractJob')) {
                throw new Exception('Job does not implement RPQ\Server\AbstractJob');
            }

            $task = new $class($this->client, $job->getId());

            return $task->perform($job->getArgs());
        } catch (JobNotFoundException $e) {
            $this->logger->error('Unable to fetch job from Redis.', [
                'jobId' => $job->getId(),
                'queueName' => $this->queue->getName(),
                'message' => $e->getMessage(),
                'code' => $e->getCode(),
                'file' => $e->getFile(),
                'line' => $e->getLine()
            ]);

            return -1;
        } catch (Exception $e) {
            $this->logger->error('An error occured when executing the job', [
                'jobId' => $job->getId(),
                'workerClass' => $job->getWorkerClass(),
                'queueName' => $this->queue->getName(),
                'message' => $e->getMessage(),
                'code' => $e->getCode(),
                'file' => $e->getFile(),
                'line' => $e->getLine()
            ]);

            return -1;
        }

        return 0;
    }
}


<?php

namespace RocknRoot\StrayFw\Database\Postgres\Query;

use RocknRoot\StrayFw\Database\Database;
use RocknRoot\StrayFw\Exception\AppException;
use RocknRoot\StrayFw\Exception\BadUse;
use RocknRoot\StrayFw\Logger;

/**
 * Representation class for PostgreSQL update queries.
 *
 * @author Nekith <nekith@errant-works.com>
 */
class Update extends Query
{
    /**
     * Update clause.
     *
     * @var string
     */
    protected $update;

    /**
     * Set clause.
     *
     * @var string|null
     */
    protected $set;

    /**
     * Where clause.
     *
     * @var Condition
     */
    protected $where;

    /**
     * Order by clause.
     *
     * @var string|null
     */
    protected $orderBy;

    /**
     * Limit clause.
     *
     * @var string
     */
    protected $limit;

    /**
     * Execute the constructed query.
     *
     * @throws AppException on SQL error
     * @return bool         true if the query is successfully executed
     */
    public function execute()
    {
        if ($this->statement == null) {
            $this->statement = Database::get($this->database)->getMasterLink()->prepare($this->toSql());
        }
        foreach ($this->parameters as $name => $value) {
            $type = \PDO::PARAM_STR;
            if (is_int($value) === true) {
                $type = \PDO::PARAM_INT;
            } elseif (is_bool($value) === true) {
                $type = \PDO::PARAM_BOOL;
            } elseif (is_null($value) === true) {
                $type = \PDO::PARAM_NULL;
            }
            $this->statement->bindValue($name, $value, $type);
        }
        $result = $this->statement->execute();
        $this->errorInfo = $this->statement->errorInfo();
        if ($this->getErrorState() != '00000') {
            Logger::get()->error('update query failed : ' . $this->getErrorMessage() . ' (' . $this->toSql() . ')');
            if (constant('STRAY_ENV') === 'development') {
                throw new AppException('update query failed : ' . $this->getErrorMessage() . ' (' . $this->toSql() . ')');
            }
        }

        return $result;
    }

    /**
     * Extract the corresponding SQL code.
     *
     * @throws BadUse if update clause has not been defined
     * @return string generated SQL code
     */
    public function toSql()
    {
        if (empty($this->update) === true) {
            throw new BadUse('update clause has not been defined (' . print_r($this, true) . ')');
        }
        $sql = 'UPDATE ' . $this->update . ' ';
        $sql .= 'SET ' . $this->set . ' ';

        if ($this->where != null) {
            $sql .= 'WHERE ' . $this->where->toSql() . ' ';
        }
        if ($this->orderBy != null) {
            $sql .= 'ORDER BY ' . $this->orderBy . ' ';
        }
        if ($this->limit != null) {
            $sql .= 'LIMIT ' . $this->limit . ' ';
        }

        return $sql;
    }

    /**
     * Set update clause.
     *
     * @param  string $table table real name
     * @return Update this
     */
    public function update($table)
    {
        $this->update = $table;

        return $this;
    }

    /**
     * Set set clause.
     *
     * @param  array|string $set set clause
     * @return Update       this
     */
    public function set($set)
    {
        if (is_array($set) === true) {
            $this->set = '';
            foreach ($set as $name => $value) {
                $pos = stripos($name, '.');
                if ($pos !== false) {
                    $this->set .= substr($name, $pos + 1);
                } else {
                    $this->set .= $name;
                }
                $this->set .= ' = ' . $value . ', ';
            }
            $this->set = substr($this->set, 0, -2);
        } else {
            $this->set = $set;
        }

        return $this;
    }

    /**
     * Set where clause.
     *
     * @param  Condition|array|string $where where clause
     * @return Update                 this
     */
    public function where($where)
    {
        $this->where = ($where instanceof Condition ? $where : new Condition($where));

        return $this;
    }

    /**
     * Set order by clause.
     *
     * @param  array|string $orderBy order by clause
     * @return Update       this
     */
    public function orderBy($orderBy)
    {
        if (is_array($orderBy) === true) {
            $this->orderBy = '';
            foreach ($orderBy as $key => $elem) {
                $this->orderBy .= $key . ' ' . $elem . ', ';
            }
            $this->orderBy = substr($this->orderBy, 0, -2);
        } else {
            $this->orderBy = $orderBy;
        }

        return $this;
    }

    /**
     * Set limit clause.
     *
     * @param  string $limit limit clause
     * @return Update this
     */
    public function limit($limit)
    {
        $this->limit = $limit;

        return $this;
    }
}


<?php

namespace RocknRoot\StrayFw\Http;

use RocknRoot\StrayFw\Config;

/**
 * Wrapper class for session variables.
 *
 * @abstract
 *
 * @author Nekith <nekith@errant-works.com>
 */
abstract class Session
{
    /**
     * True if class has already been initialized.
     *
     * @static
     * @var bool
     */
    private static $isInit = false;

    /**
     * Initialize session.
     *
     * @static
     */
    public static function init()
    {
        if (self::$isInit === false) {
            if (session_id() == null) {
                $settings = Config::getSettings();
                session_name(isset($settings['session_name']) === true ? $settings['session_name'] : 'stray_session');
                if (isset($settings['session_cookie_domain']) === true) {
                    session_set_cookie_params(0, '/', $settings['session_cookie_domain']);
                }
                session_start();
            }
            self::$isInit = true;
        }
    }

    /**
     * Get a session variable value by its key.
     *
     * @static
     * @param  string $name key
     * @return mixed
     */
    public static function get($name)
    {
        if (isset($_SESSION[$name]) === false) {
            return null;
        }

        return $_SESSION[$name];
    }

    /**
     * Check if a session variable is set.
     *
     * @static
     * @param  string $name key
     * @return bool
     */
    public static function has($name)
    {
        return isset($_SESSION[$name]);
    }

    /**
     * Set a session variable.
     *
     * @static
     * @param string $name  key
     * @param string $value new value
     */
    public static function set($name, $value)
    {
        $_SESSION[$name] = $value;
    }

    /**
     * Unset a session variable.
     *
     * @static
     * @param string $name key
     */
    public static function delete($name)
    {
        unset($_SESSION[$name]);
    }

    /**
     * Clear all session states.
     *
     * @static
     */
    public static function clear()
    {
        session_unset();
        session_destroy();
    }
}