<?php
/**
 *
 *===================================================================
 *
 *  GravatarLib - Gravatar PHP 5.3 OOP Library
 *-------------------------------------------------------------------
 * @category    gravatarlib
 * @package     gravatarlib
 * @author      emberlabs.org
 * @copyright   (c) 2011 emberlabs.org
 * @license     MIT License
 * @link        https://github.com/emberlabs/gravatarlib
 *
 *===================================================================
 *
 * This source file is subject to the MIT license that is bundled
 * with this package in the file LICENSE.
 *
 */

namespace thomaswelton\GravatarLib;
use \InvalidArgumentException;

/**
 * GravatarLib - A lightweight library for working with gravatars en masse,
 *      used for setting a ton of options and then just passing in only the essential data needed.
 *
 *
 * @category    gravatarlib
 * @package     gravatarlib
 * @author      emberlabs.org
 * @license     MIT License
 * @link        https://github.com/emberlabs/gravatarlib
 */
class Gravatar
{
	/**
	 * @var integer - The size to use for avatars.
	 */
	protected $size = 80;

	/**
	 * @var mixed - The default image to use - either a string of the gravatar-recognized default image "type" to use, a URL, or false if using the...default gravatar default image (hah)
	 */
	protected $default_image = false;

	/**
	 * @var string - The maximum rating to allow for the avatar.
	 */
	protected $max_rating = 'g';

	/**
	 * @var boolean - Should we use the secure (HTTPS) URL base?
	 */
	protected $use_secure_url = false;

	/**
	 * @var string - A temporary internal cache of the URL parameters to use.
	 */
	protected $param_cache = NULL;

	/**#@+
	 * @var string - URL constants for the avatar images
	 */
	const HTTP_URL = 'http://www.gravatar.com/avatar/';
	const HTTPS_URL = 'https://secure.gravatar.com/avatar/';
	/**#@-*/

	/**
	 * Get the currently set avatar size.
	 * @return integer - The current avatar size in use.
	 */
	public function getAvatarSize()
	{
		return $this->size;
	}

	/**
	 * Set the avatar size to use.
	 * @param integer $size - The avatar size to use, must be less than 512 and greater than 0.
	 * @return \thomaswelton\GravatarLib\Gravatar - Provides a fluent interface.
	 *
	 * @throws \InvalidArgumentException
	 */
	public function setAvatarSize($size)
	{
		// Wipe out the param cache.
		$this->param_cache = NULL;

		if(!is_int($size) && !ctype_digit($size))
		{
			throw new InvalidArgumentException('Avatar size specified must be an integer');
		}

		$this->size = (int) $size;

		if($this->size > 512 || $this->size < 0)
		{
			throw new InvalidArgumentException('Avatar size must be within 0 pixels and 512 pixels');
		}

		return $this;
	}

	/**
	 * Get the current default image setting.
	 * @return mixed - False if no default image set, string if one is set.
	 */
	public function getDefaultImage()
	{
		return $this->default_image;
	}

	/**
	 * Set the default image to use for avatars.
	 * @param mixed $image - The default image to use. Use boolean false for the gravatar default, a string containing a valid image URL, or a string specifying a recognized gravatar "default".
	 * @return \thomaswelton\GravatarLib\Gravatar - Provides a fluent interface.
	 *
	 * @throws \InvalidArgumentException
	 */
	public function setDefaultImage($image)
	{
		// Quick check against boolean false.
		if($image === false)
		{
			$this->default_image = false;

			return $this;
		}

		// Wipe out the param cache.
		$this->param_cache = NULL;

		// Check $image against recognized gravatar "defaults", and if it doesn't match any of those we need to see if it is a valid URL.
		$_image = strtolower($image);
		$valid_defaults = array('404' => 1, 'mm' => 1, 'identicon' => 1, 'monsterid' => 1, 'wavatar' => 1, 'retro' => 1);
		if(!isset($valid_defaults[$_image]))
		{
			if(!filter_var($image, FILTER_VALIDATE_URL))
			{
				throw new InvalidArgumentException('The default image specified is not a recognized gravatar "default" and is not a valid URL');
			}
			else
			{
				$this->default_image = rawurlencode($image);
			}
		}
		else
		{
			$this->default_image = $_image;
		}

		return $this;
	}

	/**
	 * Get the current maximum allowed rating for avatars.
	 * @return string - The string representing the current maximum allowed rating ('g', 'pg', 'r', 'x').
	 */
	public function getMaxRating()
	{
		return $this->max_rating;
	}

	/**
	 * Set the maximum allowed rating for avatars.
	 * @param string $rating - The maximum rating to use for avatars ('g', 'pg', 'r', 'x').
	 * @return \thomaswelton\GravatarLib\Gravatar - Provides a fluent interface.
	 *
	 * @throws \InvalidArgumentException
	 */
	public function setMaxRating($rating)
	{
		// Wipe out the param cache.
		$this->param_cache = NULL;

		$rating = strtolower($rating);
		$valid_ratings = array('g' => 1, 'pg' => 1, 'r' => 1, 'x' => 1);
		if(!isset($valid_ratings[$rating]))
		{
			throw new InvalidArgumentException(sprintf('Invalid rating "%s" specified, only "g", "pg", "r", or "x" are allowed to be used.', $rating));
		}

		$this->max_rating = $rating;

		return $this;
	}

	/**
	 * Check if we are using the secure protocol for the image URLs.
	 * @return boolean - Are we supposed to use the secure protocol?
	 */
	public function usingSecureImages()
	{
		return $this->use_secure_url;
	}

	/**
	 * Enable the use of the secure protocol for image URLs.
	 * @return \thomaswelton\GravatarLib\Gravatar - Provides a fluent interface.
	 */
	public function enableSecureImages()
	{
		$this->use_secure_url = true;

		return $this;
	}

	/**
	 * Disable the use of the secure protocol for image URLs.
	 * @return \thomaswelton\GravatarLib\Gravatar - Provides a fluent interface.
	 */
	public function disableSecureImages()
	{
		$this->use_secure_url = false;

		return $this;
	}

	/**
	 * Build the avatar URL based on the provided email address.
	 * @param string $email - The email to get the gravatar for.
	 * @param string $hash_email - Should we hash the $email variable?  (Useful if the email address has a hash stored already)
	 * @return string - The XHTML-safe URL to the gravatar.
	 */
	public function buildGravatarURL($email, $hash_email = true)
	{
		// Start building the URL, and deciding if we're doing this via HTTPS or HTTP.
		if($this->usingSecureImages())
		{
			$url = static::HTTPS_URL;
		}
		else
		{
			$url = static::HTTP_URL;
		}

		// Tack the email hash onto the end.
		if($hash_email == true && !empty($email))
		{
			$url .= $this->getEmailHash($email);
		}
		elseif(!empty($email))
		{
			$url .= $email;
		}
		else
		{
			$url .= str_repeat('0', 32);
		}

		// Check to see if the param_cache property has been populated yet
		if($this->param_cache === NULL)
		{
			// Time to figure out our request params
			$params = array();
			$params[] = 's=' . $this->getAvatarSize();
			$params[] = 'r=' . $this->getMaxRating();
			if($this->getDefaultImage())
			{
				$params[] = 'd=' . $this->getDefaultImage();
			}

			// Stuff the request params into the param_cache property for later reuse
			$this->params_cache = (!empty($params)) ? '?' . implode('&', $params) : '';
		}

		// Handle "null" gravatar requests.
		$tail = '';
		if(empty($email))
		{
			$tail = !empty($this->params_cache) ? '&f=y' : '?f=y';
		}

		// And we're done.
		return $url . $this->params_cache . $tail;
	}

	/**
	 * Get the email hash to use (after cleaning the string).
	 * @param string $email - The email to get the hash for.
	 * @return string - The hashed form of the email, post cleaning.
	 */
	public function getEmailHash($email)
	{
		// Using md5 as per gravatar docs.
		return hash('md5', strtolower(trim($email)));
	}

	/**
	 * ...Yeah, it's just an alias of buildGravatarURL.  This is just to make it easier to use as a twig asset.
	 * @see \thomaswelton\GravatarLib\Gravatar::buildGravatarURL()
	 */
	public function get($email, $hash_email = true)
	{
		// Just an alias.  Makes it easy to use this as a twig asset.
		return $this->buildGravatarURL($email, $hash_email);
	}
}


<?php
/*
 * Copyright MADE/YOUR/DAY <mail@madeyourday.co>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace MadeYourDay\SVG\IconFontGeneratorCLI;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
use MadeYourDay\SVG\IconFontGenerator;

/**
 * create-font command
 *
 * @author Martin Auswöger <martin@madeyourday.co>
 */
class CreateFontCommand extends Command{

	/**
	 * configures the create-font command
	 *
	 * @return void
	 */
	protected function configure(){
		$this
			->setName('create-font')
			->setDescription('Creates a SVG Font out of SGV files from a directory')
			->addArgument('directory', InputArgument::REQUIRED, 'path to directory containging SVG files')
			->addArgument('output-file', InputArgument::REQUIRED, 'path to the output file')
			->addOption('rename-files', null, InputOption::VALUE_NONE, 'if set, files without mapping information will be renamed to include the mapping information (e.g. my-icon.svg renamed to my-icon-xe001.svg)')
			->addOption('name', null, InputOption::VALUE_REQUIRED, 'name of the font')
		;
	}

	/**
	 * creates a SVG Font out of SGV files from a directory
	 *
	 * @param  InputInterface  $input  input
	 * @param  OutputInterface $output output
	 * @return void
	 */
	protected function execute(InputInterface $input, OutputInterface $output){

		$directory = $input->getArgument('directory');
		$outputFile = $input->getArgument('output-file');

		$generator = new IconFontGenerator;

		$output->writeln('reading files from "'.$directory.'" ...');
		$generator->generateFromDir($directory, array(
			'id' => $input->getOption('name') ?: 'SVGFont',
		), $input->getOption('rename-files'));

		$output->writeln('writing font to "'.$outputFile.'" ...');
		file_put_contents($outputFile, $generator->getFont()->getXML());

		$output->getFormatter()->setStyle('success', new OutputFormatterStyle(null, null, array('bold', 'reverse')));

		$output->writeln('<success>created '.$outputFile.' successfully</success>');

	}

}


<?php
/*
 * Copyright MADE/YOUR/DAY <mail@madeyourday.co>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace MadeYourDay\SVG\IconFontGeneratorCLI;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
use MadeYourDay\SVG\IconFontGenerator;
use MadeYourDay\SVG\Font;

/**
 * create-css command
 *
 * @author Martin Auswöger <martin@madeyourday.co>
 */
class CreateCssCommand extends Command{

	/**
	 * configures the create-css command
	 *
	 * @return void
	 */
	protected function configure(){
		$this
			->setName('create-css')
			->setDescription('Creates a CSS file with icon classes from a SVG font')
			->addArgument('font-file', InputArgument::REQUIRED, 'path to the SVG font file')
			->addArgument('output-file', InputArgument::REQUIRED, 'path where the CSS file should be saved')
		;
	}

	/**
	 * creates a CSS file with icon classes from a SVG font
	 *
	 * @param  InputInterface  $input  input
	 * @param  OutputInterface $output output
	 * @return void
	 */
	protected function execute(InputInterface $input, OutputInterface $output){

		$fontFile = realpath($input->getArgument('font-file'));
		if($fontFile === false || !file_exists($fontFile)){
			throw new \InvalidArgumentException('"'.$input->getArgument('font-file').'" does not exist');
		}

		$outputFile = $input->getArgument('output-file');

		$generator = new IconFontGenerator;

		$output->writeln('reading font file from "'.$fontFile.'" ...');
		$generator->generateFromFont(new Font(array(), file_get_contents($fontFile)));

		$output->writeln('writing CSS file to "'.$outputFile.'" ...');
		file_put_contents($outputFile, $generator->getCss());

		$output->getFormatter()->setStyle('success', new OutputFormatterStyle(null, null, array('bold', 'reverse')));

		$output->writeln('<success>created CSS file successfully</success>');

	}

}


<?php
/*
 * Copyright MADE/YOUR/DAY <mail@madeyourday.co>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace MadeYourDay\SVG\IconFontGeneratorCLI;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
use MadeYourDay\SVG\IconFontGenerator;
use MadeYourDay\SVG\Font;

/**
 * create-files command
 *
 * @author Martin Auswöger <martin@madeyourday.co>
 */
class CreateFilesCommand extends Command{

	/**
	 * configures the create-files command
	 *
	 * @return void
	 */
	protected function configure(){
		$this
			->setName('create-files')
			->setDescription('Creates single SVG files out of a SVG font and saves them to the specified directory')
			->addArgument('font-file', InputArgument::REQUIRED, 'path to the SVG font file')
			->addArgument('output-directory', InputArgument::REQUIRED, 'path to the output directory')
		;
	}

	/**
	 * creates single SVG files out of a SVG font and saves them to the specified directory
	 *
	 * @param  InputInterface  $input  input
	 * @param  OutputInterface $output output
	 * @return void
	 */
	protected function execute(InputInterface $input, OutputInterface $output){

		$fontFile = realpath($input->getArgument('font-file'));
		if($fontFile === false || !file_exists($fontFile)){
			throw new \InvalidArgumentException('"'.$input->getArgument('font-file').'" does not exist');
		}

		$outputDirectory = realpath($input->getArgument('output-directory'));
		if($outputDirectory === false || !file_exists($outputDirectory) || !is_dir($outputDirectory)){
			throw new \InvalidArgumentException('"'.$input->getArgument('output-directory').'" is no directory');
		}

		$generator = new IconFontGenerator;

		$output->writeln('reading font file from "'.$fontFile.'" ...');
		$generator->generateFromFont(new Font(array(), file_get_contents($fontFile)));

		$output->writeln('writing SVG files to "'.$outputDirectory.'" ...');
		$generator->saveGlyphsToDir($outputDirectory);

		$output->getFormatter()->setStyle('success', new OutputFormatterStyle(null, null, array('bold', 'reverse')));

		$output->writeln('<success>created SVG files successfully</success>');

	}

}


<?php
/*
 * Copyright MADE/YOUR/DAY <mail@madeyourday.co>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace MadeYourDay\SVG;

use SimpleXMLElement;

/**
 * SVG Font
 *
 * @author Martin Auswöger <martin@madeyourday.co>
 */
class Font{

	/**
	 * @var SimpleXMLElement XML document
	 */
	protected $xmlDocument;

	/**
	 * @var array font options
	 */
	protected $options = array(
		'id'           => 'SVGFont',
		'units-per-em' => 512,
		'horiz-adv-x'  => 512,
		'ascent'       => 448,
		'descent'      => -64,
		'x-height'     => 240,
		'cap-height'   => 480,
	);

	/**
	 * create a empty font or from a SVG XML string
	 *
	 * @param array  $options   font options
	 * @param string $svgString SVG XML string
	 */
	public function __construct($options = array(), $svgString = null){

		$newFont = false;
		if($svgString === null){
			$newFont = true;
			$svgString = '<?xml version="1.0" standalone="no"?>
				<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
				<svg xmlns="http://www.w3.org/2000/svg"></svg>';
		}

		$this->xmlDocument = new SimpleXMLElement($svgString);

		if(!count($this->xmlDocument->defs)){
			$this->xmlDocument->addChild('defs');
		}
		if(!count($this->xmlDocument->defs[0]->font)){
			$this->xmlDocument->defs[0]->addChild('font');
		}
		if(!count($this->xmlDocument->defs[0]->font[0]->{'font-face'})){
			$this->xmlDocument->defs[0]->font[0]->addChild('font-face');
		}
		if(!count($this->xmlDocument->defs[0]->font[0]->{'missing-glyph'})){
			$this->xmlDocument->defs[0]->font[0]->addChild('missing-glyph');
		}

		if(!$newFont){
			$options = array_merge($this->getOptionsFromXML(), $options);
		}

		$this->setOptions($options);

	}

	/**
	 * set font optinos
	 *
	 * @param array $options font options
	 */
	public function setOptions($options = array()){

		$this->options = array_merge($this->options, $options);

		$this->xmlDocument->defs[0]->font[0]['id'] = $this->options['id'];
		$this->xmlDocument->defs[0]->font[0]['horiz-adv-x'] = $this->options['horiz-adv-x'];
		$this->xmlDocument->defs[0]->font[0]->{'font-face'}[0]['units-per-em'] = $this->options['units-per-em'];
		$this->xmlDocument->defs[0]->font[0]->{'font-face'}[0]['ascent'] = $this->options['ascent'];
		$this->xmlDocument->defs[0]->font[0]->{'font-face'}[0]['descent'] = $this->options['descent'];
		$this->xmlDocument->defs[0]->font[0]->{'font-face'}[0]['x-height'] = $this->options['x-height'];
		$this->xmlDocument->defs[0]->font[0]->{'font-face'}[0]['cap-height'] = $this->options['cap-height'];
		$this->xmlDocument->defs[0]->font[0]->{'missing-glyph'}[0]['horiz-adv-x'] = $this->options['horiz-adv-x'];

	}

	/**
	 * returns font options stored in the XML document
	 *
	 * @return array font options
	 */
	protected function getOptionsFromXML(){

		$options = array();

		foreach(array('id', 'horiz-adv-x') as $key){
			if(isset($this->xmlDocument->defs[0]->font[0][$key])){
				$options[$key] = (string)$this->xmlDocument->defs[0]->font[0][$key];
			}
		}
		foreach(array('units-per-em', 'ascent', 'descent', 'x-height', 'cap-height') as $key){
			if(isset($this->xmlDocument->defs[0]->font[0]->{'font-face'}[0][$key])){
				$options[$key] = (string)$this->xmlDocument->defs[0]->font[0]->{'font-face'}[0][$key];
			}
		}

		return $options;

	}

	/**
	 * get font options
	 *
	 * @return array font options
	 */
	public function getOptions(){
		return $this->options;
	}

	/**
	 * get XML string
	 *
	 * @return string XML SVG string
	 */
	public function getXML(){
		return $this->xmlDocument->asXML();
	}

	/**
	 * add a glyph to the font
	 *
	 * @param string $char  character of the glyph
	 * @param string $path  SVG path definition
	 * @param string $name  name of the glyph
	 * @param float  $width glyph width (horiz-adv-x)
	 */
	public function addGlyph($char, $path, $name = null, $width = null){

		$glyph = $this->xmlDocument->defs[0]->font[0]->addChild('glyph');
		$glyph->addAttribute('unicode', $char);
		if($name !== null){
			$glyph->addAttribute('glyph-name', $name);
		}
		if($width !== null){
			$glyph->addAttribute('horiz-adv-x', $width);
		}
		$glyph->addAttribute('d', $path);

	}

	/**
	 * get all glyphs
	 *
	 * @return array set of glyph arrays containing char, path, name and width (name and with are optional)
	 */
	public function getGlyphs(){

		if(
			!isset($this->xmlDocument->defs[0]->font[0]->glyph) ||
			!count($this->xmlDocument->defs[0]->font[0]->glyph)
		){
			return array();
		}
		$glyphs = array();
		foreach($this->xmlDocument->defs[0]->font[0]->glyph as $xmlGlyph){
			if(isset($xmlGlyph['unicode']) && isset($xmlGlyph['d'])){
				$glyph = array(
					'char' => (string)$xmlGlyph['unicode'],
					'path' => (string)$xmlGlyph['d'],
				);
				if(isset($xmlGlyph['glyph-name'])){
					$glyph['name'] = (string)$xmlGlyph['glyph-name'];
				}
				if(isset($xmlGlyph['horiz-adv-x'])){
					$glyph['width'] = (string)$xmlGlyph['horiz-adv-x'];
				}
				$glyphs[] = $glyph;
			}
		}
		return $glyphs;
	}

}


<?php
/*
 * Copyright MADE/YOUR/DAY <mail@madeyourday.co>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace MadeYourDay\SVG;

/**
 * SVG Icon Font Generator
 *
 * @author Martin Auswöger <martin@madeyourday.co>
 */
class IconFontGenerator{

	/**
	 * @var Font
	 */
	protected $font;

	/**
	 * @var array mapping information file path and name mapped to glyph character
	 */
	protected $mapping = array();

	/**
	 * generate a font from a directory containing svg files (one file per glyph)
	 * by naming convention the file name can specify which character should be mapped to the glyph
	 * - my-icon.png       => creates a glyph with the name 'my-icon' mapped to a character in the Unicode Private Use Area
	 * - my-icon-x263a.png => creates a glyph with the name 'my-icon' mapped to the unicode character U+263A "☺"
	 *
	 * @param  string  $path              SVG path definition (consider the font coordinate system has an inverted y axis)
	 * @param  array   $fontOptions       font options for the Font object
	 * @param  boolean $renameSourceFiles if true, files without mapping information will be renamed
	 * @return static                     this
	 */
	public function generateFromDir($path, $fontOptions = array(), $renameSourceFiles = false){

		$this->font = new Font($fontOptions);
		$this->mapping = array();

		$fontOptions = $this->font->getOptions();

		$files = scandir($path);
		foreach($files as $fileName){
			if(strtolower(substr($fileName, -4)) === '.svg'){
				if(preg_match('(^(.*)-x([0-9a-f]{2,6})\\.svg$)i', $fileName, $matches)){
					$iconName = strtolower($matches[1]);
					$iconCode = static::hexToUnicode(strtolower($matches[2]));
					if(isset($this->mapping[$iconCode])){
						throw new \Exception('Duplicate glyph '.$iconCode.' '.$iconName);
					}
					$this->mapping[$iconCode] = array(
						'path' => $path.DIRECTORY_SEPARATOR.$fileName,
						'name' => $iconName,
					);
				}
			}
		}
		foreach($files as $fileName){
			if(strtolower(substr($fileName, -4)) === '.svg'){
				if(!preg_match('(^(.*)-x([0-9a-f]{2,6})\\.svg$)i', $fileName)){
					$iconName = strtolower(substr($fileName, 0, -4));
					$code = hexdec('e001');
					while(isset($this->mapping[static::hexToUnicode(dechex($code))])){
						$code++;
					}
					if($renameSourceFiles){
						if(!rename($path.DIRECTORY_SEPARATOR.$fileName, $path.DIRECTORY_SEPARATOR.$iconName.'-x'.dechex($code).'.svg')){
							throw new \Exception('unable to rename "'.$path.DIRECTORY_SEPARATOR.$fileName.'"');
						}
						$fileName = $iconName.'-x'.dechex($code).'.svg';
					}
					$this->mapping[static::hexToUnicode(dechex($code))] = array(
						'path' => $path.DIRECTORY_SEPARATOR.$fileName,
						'name' => $iconName,
					);
				}
			}
		}
		foreach($this->mapping as $code => $icon){
			try{
				$iconDoc = new Document(file_get_contents($icon['path']));
				$viewBox = $iconDoc->getViewBox();
				$this->font->addGlyph(
					$code,
					$iconDoc->getPath($fontOptions['units-per-em']/$viewBox['height'], null, 'vertical', true, 0, $fontOptions['descent']),
					$icon['name'],
					round($viewBox['width']*$fontOptions['units-per-em']/$viewBox['height'])
				);
			}
			catch(\Exception $e){
				throw new \Exception(basename($icon['path']).': '.$e->getMessage());
			}
		}

		return $this;

	}

	/**
	 * generate a font based on a existing SVG font
	 *
	 * @param  Font   $font font object
	 * @return static       this
	 */
	public function generateFromFont(Font $font){

		$this->mapping = array();
		$this->font = $font;
		return $this;

	}

	/**
	 * get the font object
	 *
	 * @return Font font object
	 */
	public function getFont(){
		return $this->font;
	}

	/**
	 * get glyph names
	 *
	 * @return array array with the characters (in hex) as keys and the glyph names as values
	 */
	public function getGlyphNames(){

		$glyphNames = array();
		foreach($this->font->getGlyphs() as $glyph){
			$glyphNames[static::unicodeToHex($glyph['char'])] = empty($glyph['name']) ? null : $glyph['name'];
		}
		return $glyphNames;

	}

	/**
	 * get css definition for icon class names
	 *
	 * @return string css class definitions
	 */
	public function getCss(){

		$css = '';
		foreach($this->getGlyphNames() as $unicode => $name){
			$css .= ".icon-".$name.":before {"."\n";
			$css .= "\tcontent: \"\\".$unicode."\";\n";
			$css .= "}\n";
		}
		return $css;

	}

	/**
	 * save the current font as single svg files in a directory (counterpart of generateFromDir)
	 *
	 * @param  string $dir directory path
	 * @return static      this
	 */
	public function saveGlyphsToDir($dir){

		$fontOptions = $this->font->getOptions();

		$svgTemplate = '<?xml version="1.0" encoding="utf-8"?>'."\n".
			'<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">'."\n".
			'<svg'."\n".
			'	version="1.1"'."\n".
			'	id="Layer_1"'."\n".
			'	xmlns="http://www.w3.org/2000/svg"'."\n".
			'	xmlns:xlink="http://www.w3.org/1999/xlink"'."\n".
			'	x="0px"'."\n".
			'	y="0px"'."\n".
			'	width="%%%WIDTH%%%px"'."\n".
			'	height="512px"'."\n".
			'	viewBox="0 0 %%%WIDTH%%% 512"'."\n".
			'	enable-background="new 0 0 512 512"'."\n".
			'	xml:space="preserve"'."\n".
			'>'."\n".
			'	<g id="Grid">'."\n".
			'		<rect x="0" fill="none" stroke="#A9CCDB" stroke-miterlimit="10" width="512" height="512"/>'."\n";
		for($i = 32; $i < 512; $i += 32){
			$color = 'A9CCDB';
			if($i === 448){
				$color = 'FF0000';
			}
			$svgTemplate .= '		<line fill="none" stroke="#'.$color.'" stroke-miterlimit="10" x1="0" y1="'.$i.'" x2="512" y2="'.$i.'"/>'."\n";
		}
		for($i = 32; $i < 512; $i += 32){
			$svgTemplate .= '		<line fill="none" stroke="#A9CCDB" stroke-miterlimit="10" x1="'.$i.'" y1="0" x2="'.$i.'" y2="512"/>'."\n";
		}
		$svgTemplate .= '	</g>'."\n".
			'	<path d="%%%PATH%%%"/>'."\n".
			'</svg>'."\n";

		if(!is_dir($dir)){
			throw new \InvalidArgumentException('$dir must be a writable directory');
		}

		foreach($this->font->getGlyphs() as $glyph){

			$targetPath = $dir.DIRECTORY_SEPARATOR.(empty($glyph['name']) ? 'icon' : preg_replace('([^a-z0-9]+)i', '-', $glyph['name'])).'-x'.static::unicodeToHex($glyph['char']).'.svg';

			if(isset($this->mapping[$glyph['char']])){

				if(!copy($this->mapping[$glyph['char']]['path'], $targetPath)){
					throw new \Exception('unable to copy "'.$this->mapping[$glyph['char']]['path'].'" to "'.$targetPath.'"');
				}

			}
			else{

				$glyphDocument = Document::createFromPath($glyph['path'], $fontOptions['horiz-adv-x'], $fontOptions['units-per-em']);
				if(file_put_contents(
					$targetPath,
					str_replace(array('%%%PATH%%%', '%%%WIDTH%%%'), array(
						$glyphDocument->getPath(512/$fontOptions['units-per-em'], null, 'vertical', true, 0, -64),
						empty($glyph['width']) ? 512 : ($glyph['width']*512/$fontOptions['units-per-em'])
					), $svgTemplate)
				) === false){
					throw new \Exception('unable to write to "'.$targetPath.'"');
				}

			}

		}

		return $this;

	}

	/**
	 * get the unicode hex representation of a unicode character
	 *
	 * @param  string $char single character encoded as UTF-8
	 * @return string       unicode hex representation of the character
	 */
	public static function unicodeToHex($char){

		if(!is_string($char) || mb_strlen($char, 'utf-8') !== 1){
			throw new \InvalidArgumentException('$char must be one single character');
		}

		$unicode = unpack('N', mb_convert_encoding($char, 'UCS-4BE', 'UTF-8'));
		return dechex($unicode[1]);

	}

	/**
	 * get the unicode character of a unicode hex string
	 *
	 * @param  string $char unicode hex representation (e.g. "263a")
	 * @return string       single character encoded as UTF-8
	 */
	protected static function hexToUnicode($char){

		if(!is_string($char) || !preg_match('(^[0-9a-f]{2,6}$)i', $char)){
			throw new \InvalidArgumentException('$char must be one single unicode character as hex string');
		}

		return mb_convert_encoding('&#x'.strtolower($char).';', 'UTF-8', 'HTML-ENTITIES');

	}

}


<?php
/*
 * Copyright MADE/YOUR/DAY <mail@madeyourday.co>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace MadeYourDay\SVG;

use SimpleXMLElement;

/**
 * SVG Document
 *
 * @author Martin Auswöger <martin@madeyourday.co>
 */
class Document{

	/**
	 * @var SimpleXMLElement XML document element
	 */
	protected $xmlDocument;

	/**
	 * @var array conversion table between several units and pixels
	 */
	protected $unitInPixels = array(
		'px' => 1,
		'pt' => 1.25,
		'mm' => 3.543307096633,
		'pc' => 15,
		'cm' => 35.43307096633,
		'in' => 90,
	);

	/**
	 * creates a Document instance from a xml string
	 *
	 * @param string $svgString XML SVG content
	 */
	public function __construct($svgString){

		$this->xmlDocument = new SimpleXMLElement($svgString);

	}

	/**
	 * creates a Document instance from a simple SVG path definition
	 *
	 * @param  string  $path   SVG path definition
	 * @param  integer $width  document width (default 512)
	 * @param  integer $height document height (default 512)
	 * @return static          Document instance
	 */
	public static function createFromPath($path, $width = 512, $height = 512){

		return new static('<?xml version="1.0" encoding="utf-8"?>'.
			'<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">'.
			'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="'.$width.'" height="'.$height.'">'.
			'<path d="'.$path.'"/>'.
			'</svg>');

	}

	/**
	 * returns an viewbox array containing x/y offsets and with/height
	 *
	 * @return array|null array with x,y,width,height values
	 */
	public function getViewBox(){

		if(!empty($this->xmlDocument['viewBox'])){

			$viewBox = $this->getValuesFromList($this->xmlDocument['viewBox']);
			if(count($viewBox) !== 4){
				return null;
			}
			return array(
				'x' => $viewBox[0]*1,
				'y' => $viewBox[1]*1,
				'width' => $viewBox[2]*1,
				'height' => $viewBox[3]*1,
			);

		}

		if(!empty($this->xmlDocument['width']) && !empty($this->xmlDocument['height'])){

			$width = trim($this->xmlDocument['width']);
			$height = trim($this->xmlDocument['height']);
			if(isset($this->unitInPixels[substr($width, -2)])){
				$width = $width*$this->unitInPixels[substr($width, -2)];
			}
			if(isset($this->unitInPixels[substr($height, -2)])){
				$height = $height*$this->unitInPixels[substr($height, -2)];
			}
			return array(
				'x' => 0,
				'y' => 0,
				'width' => $width*1,
				'height' => $height*1,
			);

		}

		return null;

	}

	/**
	 * returns one single SVG path definition for all elements in the document
	 *
	 * @param  float   $scale          a positive number how much the path should be scaled (1 means 100%)
	 * @param  integer $roundPrecision number of decimal digits to round to or null to disable rounding
	 * @param  string  $flip           'none', 'horizontal' or 'vertical' (requires a valid view box)
	 * @param  boolean $onlyFilled     ignore non filled objects
	 * @param  integer $xOffset        x offset
	 * @param  integer $yOffset        y offset
	 * @return string                  SVG path definition
	 */
	public function getPath($scale = 1, $roundPrecision = null, $flip = 'none', $onlyFilled = true, $xOffset = 0, $yOffset = 0){

		$path = $this->getPathPart($this->xmlDocument, $onlyFilled);

		if($scale !== 1 || $roundPrecision !== null || $flip !== 'none' || $xOffset !== 0 || $yOffset !== 0){
			$path = $this->transformPath($path, $scale, $roundPrecision, $flip, $xOffset / $scale, $yOffset / $scale);
		}

		return trim($path);

	}

	/**
	 * returns one single SVG path definition for all elements in the specified element
	 *
	 * @param  SimpleXMLElement $xmlElement group or svg element
	 * @param  boolean          $onlyFilled ignore non filled objects
	 * @return string                       SVG path definition
	 */
	protected function getPathPart(SimpleXMLElement $xmlElement, $onlyFilled){

		$path = '';

		if($xmlElement === null){
			$xmlElement = $this->xmlDocument;
		}

		foreach($xmlElement->children() as $child){
			$childName = $child->getName();
			if(!empty($child['transform'])){
				throw new \Exception('Transforms are currently not supported!');
			}
			if($childName === 'g'){
				$path .= ' '.$this->getPathPart($child, $onlyFilled);
			}
			else{
				if($onlyFilled && (string)$child['fill'] === 'none'){
					continue;
				}
				if($childName === 'polygon'){
					$path .= ' '.$this->getPathFromPolygon($child);
				}
				elseif($childName === 'rect'){
					$path .= ' '.$this->getPathFromRect($child);
				}
				elseif($childName === 'circle'){
					$path .= ' '.$this->getPathFromCircle($child);
				}
				elseif($childName === 'ellipse'){
					$path .= ' '.$this->getPathFromEllipse($child);
				}
				elseif($childName === 'path'){
					$pathPart = trim($child['d']);
					if(substr($pathPart, 0, 1) === 'm'){
						$pathPart = 'M'.substr($pathPart, 1);
					}
					$path .= ' '.$pathPart;
				}
			}
		}

		return trim($path);

	}

	/**
	 * transforms a SVG path definition by the given parameters
	 *
	 * @param  string  $path           SVG path definition
	 * @param  float   $scale          a positive number how much the path should be scaled (1 means 100%)
	 * @param  integer $roundPrecision number of decimal digits to round to or null to disable rounding
	 * @param  string  $flip           'none', 'horizontal' or 'vertical' (requires a valid view box)
	 * @param  integer $xOffset        x offset
	 * @param  integer $yOffset        y offset
	 * @return string                  SVG path definition
	 */
	protected function transformPath($path, $scale, $roundPrecision, $flip, $xOffset, $yOffset){

		if($flip === 'horizontal' || $flip === 'vertical'){
			$viewBox = $this->getViewBox();
		}

		return preg_replace_callback('([m,l,h,v,c,s,q,t,a,z](?:[\\s,]*-?(?=\\.?\\d)\\d*(?:\\.\\d+)?)*)i', function($maches) use ($scale, $roundPrecision, $flip, $xOffset, $yOffset, $viewBox){

			$command = substr($maches[0], 0, 1);
			$absoluteCommand = strtoupper($command) === $command;
			$xyCommand = in_array(strtolower($command), array('m','l','c','s','q','t'));
			$xCommand = strtolower($command) === 'h';
			$yCommand = strtolower($command) === 'v';
			if(strtolower($command) === 'z'){
				return $command;
			}
			if(strtolower($command) === 'a'){
				throw new \Exception('Path command "A" is currently not supportet!');
			}
			$values = $this->getValuesFromList(substr($maches[0], 1));

			foreach($values as $key => $value) {

				if(
					$flip === 'horizontal' &&
					((!($key%2) && $xyCommand) || $xCommand)
				){
					$values[$key] *= -1;
					if($absoluteCommand){
						$values[$key] += $viewBox['width'];
					}
				}
				if(
					$flip === 'vertical' &&
					(($key%2 && $xyCommand) || $yCommand)
				){
					$values[$key] *= -1;
					if($absoluteCommand){
						$values[$key] += $viewBox['height'];
					}
				}
				if(
					$absoluteCommand &&
					((!($key%2) && $xyCommand) || $xCommand)
				){
					$values[$key] += $xOffset;
				}
				if(
					$absoluteCommand &&
					(($key%2 && $xyCommand) || $yCommand)
				){
					$values[$key] += $yOffset;
				}
				$values[$key] *= $scale;
				if($roundPrecision !== null){
					$values[$key] = round($values[$key], $roundPrecision);
				}

			}

			return $command.implode(' ', $values);

		}, $path);

	}

	/**
	 * converts a polygon object to a SVG path definition
	 *
	 * @param  SimpleXMLElement $polygon polygon element
	 * @return string                    SVG path definition
	 */
	protected function getPathFromPolygon(SimpleXMLElement $polygon){

		$points = $this->getValuesFromList($polygon['points']);
		$path = 'M'.array_shift($points).' '.array_shift($points);
		while(count($points)){
			$path .= 'L'.array_shift($points).' '.array_shift($points);
		}
		return $path.'Z';

	}

	/**
	 * converts a rect object to a SVG path definition
	 *
	 * @param  SimpleXMLElement $rect rect element
	 * @return string                 SVG path definition
	 */
	protected function getPathFromRect(SimpleXMLElement $rect){

		if(empty($rect['width']) || $rect['width'] < 0 || empty($rect['height']) || $rect['height'] < 0){
			return '';
		}
		if(empty($rect['x'])){
			$rect['x'] = 0;
		}
		if(empty($rect['y'])){
			$rect['y'] = 0;
		}
		return 'M'.$rect['x'].' '.$rect['y'].'l'.$rect['width'].' 0l0 '.$rect['height'].'l'.(-$rect['width']).' 0Z';

	}

	/**
	 * converts a circle object to a SVG path definition
	 *
	 * @param  SimpleXMLElement $circle circle element
	 * @return string                   SVG path definition
	 */
	protected function getPathFromCircle(SimpleXMLElement $circle){

		$mult = 0.55228475;
		return
			'M'.($circle['cx']-$circle['r']).' '.$circle['cy'].
			'C'.($circle['cx']-$circle['r']).' '.($circle['cy']-$circle['r']*$mult).' '.($circle['cx']-$circle['r']*$mult).' '.($circle['cy']-$circle['r']).' '.$circle['cx'].' '.($circle['cy']-$circle['r']).
			'C'.($circle['cx']+$circle['r']*$mult).' '.($circle['cy']-$circle['r']).' '.($circle['cx']+$circle['r']).' '.($circle['cy']-$circle['r']*$mult).' '.($circle['cx']+$circle['r']).' '.$circle['cy'].
			'C'.($circle['cx']+$circle['r']).' '.($circle['cy']+$circle['r']*$mult).' '.($circle['cx']+$circle['r']*$mult).' '.($circle['cy']+$circle['r']).' '.$circle['cx'].' '.($circle['cy']+$circle['r']).
			'C'.($circle['cx']-$circle['r']*$mult).' '.($circle['cy']+$circle['r']).' '.($circle['cx']-$circle['r']).' '.($circle['cy']+$circle['r']*$mult).' '.($circle['cx']-$circle['r']).' '.$circle['cy'].
			'Z';

	}

	/**
	 * converts a ellipse object to a SVG path definition
	 *
	 * @param  SimpleXMLElement $ellipse ellipse element
	 * @return string                    SVG path definition
	 */
	protected function getPathFromEllipse(SimpleXMLElement $ellipse){

		$mult = 0.55228475;
		return
			'M'.($ellipse['cx']-$ellipse['rx']).' '.$ellipse['cy'].
			'C'.($ellipse['cx']-$ellipse['rx']).' '.($ellipse['cy']-$ellipse['ry']*$mult).' '.($ellipse['cx']-$ellipse['rx']*$mult).' '.($ellipse['cy']-$ellipse['ry']).' '.$ellipse['cx'].' '.($ellipse['cy']-$ellipse['ry']).
			'C'.($ellipse['cx']+$ellipse['rx']*$mult).' '.($ellipse['cy']-$ellipse['ry']).' '.($ellipse['cx']+$ellipse['rx']).' '.($ellipse['cy']-$ellipse['ry']*$mult).' '.($ellipse['cx']+$ellipse['rx']).' '.$ellipse['cy'].
			'C'.($ellipse['cx']+$ellipse['rx']).' '.($ellipse['cy']+$ellipse['ry']*$mult).' '.($ellipse['cx']+$ellipse['rx']*$mult).' '.($ellipse['cy']+$ellipse['ry']).' '.$ellipse['cx'].' '.($ellipse['cy']+$ellipse['ry']).
			'C'.($ellipse['cx']-$ellipse['rx']*$mult).' '.($ellipse['cy']+$ellipse['ry']).' '.($ellipse['cx']-$ellipse['rx']).' '.($ellipse['cy']+$ellipse['ry']*$mult).' '.($ellipse['cx']-$ellipse['rx']).' '.$ellipse['cy'].
			'Z';

	}

	/**
	 * converts a string list into single values
	 *
	 * Should match 123,-123 1.23-1.23 as four numbers (123, -123, 1.23, -1.23)
	 * Should match 1.2.3 as two numbers (1.2, 0.3)
	 * Should match -.1.2.3 as three numbers (-0.1, 0.2, 0.3)
	 *
	 * @param  string $list
	 * @return string[]
	 */
	protected function getValuesFromList($list){

		if(preg_match_all('(-?(?=\\.?\\d)\\d*(?:\\.\\d+)?)i', $list, $values)){
			return $values[0];
		}
		return array();

	}

}


<?php
/*
 * Copyright MADE/YOUR/DAY <mail@madeyourday.co>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace MadeYourDay\SVG\IconFontGeneratorCLI;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
use MadeYourDay\SVG\IconFontGenerator;
use MadeYourDay\SVG\Font;

/**
 * create-info command
 *
 * @author Martin Auswöger <martin@madeyourday.co>
 */
class CreateInfoCommand extends Command{

	/**
	 * configures the create-info command
	 *
	 * @return void
	 */
	protected function configure(){
		$this
			->setName('create-info')
			->setDescription('Creates a HTML info page out of a SVG font')
			->addArgument('font-file', InputArgument::REQUIRED, 'path to the SVG font file')
			->addArgument('output-file', InputArgument::REQUIRED, 'path where the HTML page should be saved')
			->addOption('as-list', 'l', InputOption::VALUE_NONE, 'if set the generated HTML file will be a simple unordered list instead of a full HTML document')
		;
	}

	/**
	 * creates a HTML info page out of a SVG font
	 *
	 * @param  InputInterface  $input  input
	 * @param  OutputInterface $output output
	 * @return void
	 */
	protected function execute(InputInterface $input, OutputInterface $output){

		$fontFile = realpath($input->getArgument('font-file'));
		if($fontFile === false || !file_exists($fontFile)){
			throw new \InvalidArgumentException('"'.$input->getArgument('font-file').'" does not exist');
		}

		$outputFile = $input->getArgument('output-file');

		$generator = new IconFontGenerator;

		$output->writeln('reading font file from "'.$fontFile.'" ...');
		$generator->generateFromFont(new Font(array(), file_get_contents($fontFile)));

		$output->writeln('writing HTML file to "'.$outputFile.'" ...');
		if ($input->getOption('as-list')) {
			$html = $this->getHTMLListFromGenerator($generator, basename($fontFile));
		}
		else {
			$html = $this->getHTMLFromGenerator($generator, basename($fontFile));
		}
		file_put_contents($outputFile, $html);

		$output->getFormatter()->setStyle('success', new OutputFormatterStyle(null, null, array('bold', 'reverse')));

		$output->writeln('<success>created HTML info page successfully</success>');

	}

	/**
	 * creates the HTML for the info page
	 *
	 * @param  IconFontGenerator $generator icon font generator
	 * @param  string            $fontFile  font file name
	 * @return string                       HTML for the info page
	 */
	protected function getHTMLFromGenerator(IconFontGenerator $generator, $fontFile){

		$fontOptions = $generator->getFont()->getOptions();

		$html = '<!doctype html>

			<title>'.htmlspecialchars($fontOptions['id']).'</title>

';

		$glyphNames = $generator->getGlyphNames();
		asort($glyphNames);

		foreach($glyphNames as $unicode => $glyph){
			$html .= '

icon-'.$glyph.'

				<input type="text" readonly="readonly" value="&#x'.$unicode.';" />
				<input type="text" readonly="readonly" value="\\'.$unicode.'" />
				<input type="text" readonly="readonly" value="&#x'.$unicode.';" class="icon-input" />

';
		}

		$html .= '

		';

		return $html;

	}

	/**
	 * creates a HTML list
	 *
	 * @param  IconFontGenerator $generator icon font generator
	 * @param  string            $fontFile  font file name
	 * @return string                       HTML unordered list
	 */
	protected function getHTMLListFromGenerator(IconFontGenerator $generator, $fontFile){

		$fontOptions = $generator->getFont()->getOptions();

		$html = '
';

		$glyphNames = $generator->getGlyphNames();
		asort($glyphNames);

		foreach($glyphNames as $unicode => $glyph){
			$html .= "\n\t" .
				'
' .
				htmlspecialchars($glyph) . '
';
		}

		$html .= "\n" . '
' . "\n";

		return $html;

	}

}


<?php

namespace PragmaRX\Recovery;

use PragmaRX\Random\Random;

class Recovery
{
    protected $codes = [];

    protected $count = 8;

    protected $blocks = 2;

    protected $chars = 10;

    protected $random;

    protected $blockSeparator = '-';

    private $collectionFunction = 'collect';

    /**
     * Recovery constructor.
     *
     * @param Random $random
     */
    public function __construct(Random $random = null)
    {
        if (is_null($random)) {
            $random = new Random();
        }

        $this->random = $random;
    }

    /**
     * Set to alpha codes.
     *
     * @return Recovery
     */
    public function alpha()
    {
        $this->random->alpha();

        return $this;
    }

    /**
     * Generate the recovery codes.
     *
     * @return array
     */
    protected function generate()
    {
        $this->reset();

        foreach (range(1, $this->getCount()) as $counter) {
            $this->codes[] = $this->generateBlocks();
        }

        return $this->codes;
    }

    /**
     * Generate all blocks.
     *
     * @return array
     */
    protected function generateBlocks()
    {
        $blocks = [];

        foreach (range(1, $this->getBlocks()) as $counter) {
            $blocks[] = $this->generateChars();
        }

        return implode($this->blockSeparator, $blocks);
    }

    /**
     * Generate random chars.
     *
     * @return string
     */
    protected function generateChars()
    {
        return $this->random->size($this->getChars())->get();
    }

    /**
     * Check if codes must be generated.
     *
     * @return bool
     */
    protected function mustGenerate()
    {
        return count($this->codes) == 0;
    }

    /**
     * Set lowercase codes state.
     *
     * @param bool $state
     * @return Recovery
     */
    public function lowercase($state = true)
    {
        $this->random->lowercase($state);

        return $this;
    }

    /**
     * Set the block separator.
     *
     * @param string $blockSeparator
     * @return Recovery
     */
    public function setBlockSeparator($blockSeparator)
    {
        $this->blockSeparator = $blockSeparator;

        return $this;
    }

    /**
     * Set the collection function.
     *
     * @param string $collectionFunction
     * @return Recovery
     */
    public function collectionFunction($collectionFunction)
    {
        $this->collectionFunction = $collectionFunction;

        return $this;
    }

    /**
     * Set uppercase codes state.
     *
     * @param bool $state
     * @return Recovery
     */
    public function uppercase($state = true)
    {
        $this->random->uppercase($state);

        return $this;
    }

    /**
     * Set mixedcase codes state.
     *
     * @return Recovery
     */
    public function mixedcase()
    {
        $this->random->mixedcase();

        return $this;
    }

    /**
     * Set to numeric codes.
     *
     * @return Recovery
     */
    public function numeric()
    {
        $this->random->numeric();

        return $this;
    }

    /**
     * Get an array of recovery codes.
     *
     * @return array
     */
    public function toArray()
    {
        if ($this->mustGenerate()) {
            return $this->generate();
        }

        return $this->getCodes();
    }

    /**
     * Get a collection of recovery codes.
     *
     * @return array
     * @throws \Exception
     */
    public function toCollection()
    {
        if (function_exists($this->collectionFunction)) {
            return call_user_func($this->collectionFunction, $this->toArray());
        }

        throw new \Exception(
            "Function {$this->collectionFunction}() was not found. " .
            "You probably need to install a suggested package?"
        );
    }

    /**
     * Get a json of recovery codes.
     *
     * @return array
     */
    public function toJson()
    {
        return json_encode($this->toArray());
    }

    /**
     * Get the blocks size.
     *
     * @return int
     */
    public function getBlocks()
    {
        return $this->blocks;
    }

    /**
     * Get the chars count.
     *
     * @return int
     */
    public function getChars()
    {
        return $this->chars;
    }

    /**
     * Get the codes.
     *
     * @return array
     */
    public function getCodes()
    {
        return $this->codes;
    }

    /**
     * Get the codes count.
     *
     * @return int
     */
    public function getCount()
    {
        return $this->count;
    }

    /**
     * Reset generated codes.
     *
     */
    protected function reset()
    {
        $this->codes = [];
    }

    /**
     * Set the blocks size.
     *
     * @param int $blocks
     * @return Recovery
     */
    public function setBlocks($blocks)
    {
        $this->blocks = $blocks;

        $this->reset();

        return $this;
    }

    /**
     * Set the chars count.
     *
     * @param int $chars
     * @return Recovery
     */
    public function setChars($chars)
    {
        $this->chars = $chars;

        $this->reset();

        return $this;
    }

    /**
     * Set the codes count.
     *
     * @param int $count
     * @return Recovery
     */
    public function setCount($count)
    {
        $this->count = $count;

        $this->reset();

        return $this;
    }
}


<?php
/**
 * The MIT License
 *
 * Copyright (c) 2015 Nikolay Bondarenko <misterionkell@gmail.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 * PHP version 5.4
 *
 * @package Ko
 * @author Nikolay Bondarenko
 * @copyright 2015 Nikolay Bondarenko. All rights reserved.
 * @license MIT http://opensource.org/licenses/MIT
 */

namespace Ko;

/**
 * Class Process
 *
 * @package Ko
 * @author Nikolay Bondarenko <misterionkell@gmail.com>
 * @copyright 2015 Nikolay Bondarenko. All rights reserved.
 * @version 1.0
 */
class Process implements \ArrayAccess, \Countable
{
    use Mixin\ProcessTitle;
    use Mixin\EventEmitter {
        on as protected internalOn;
        emit as protected internalEmit;
    }

    /**
     * Shared memory internal variables used to mark process as completely initialized.
     *
     * @internal
     */
    const STARTED_MARKER = '__started';

    /**
     * Wait ready timeout in milliseconds.
     *
     * @internal
     */
    const WAIT_IDLE = 1000;

    /**
     * Posix process id.
     *
     * @var int
     */
    protected $pid;

    /**
     * @var int
     */
    protected $status;

    /**
     * @var int
     */
    protected $exitCode;

    /**
     * @var callable
     */
    protected $callable;

    /**
     * @var bool
     */
    protected $shouldShutdown;
    /**
     * @var bool
     */
    protected $running;

    /**
     * @var SharedMemory
     */
    protected $sharedMemory;

    /**
     * @var SignalHandler
     */
    protected $signalHandler;

    public function __construct(callable $callable)
    {
        $this->pid = 0;
        $this->callable = $callable;
        $this->shouldShutdown = false;
    }

    /**
     * @return \Ko\SharedMemory
     */
    public function getSharedMemory()
    {
        if ($this->sharedMemory === null) {
            $this->setSharedMemory(new SharedMemory());
        }

        return $this->sharedMemory;
    }

    /**
     * @param \Ko\SharedMemory $sharedMemory
     *
     * @return $this
     */
    public function setSharedMemory(SharedMemory $sharedMemory)
    {
        $this->sharedMemory = $sharedMemory;
        return $this;
    }

    /**
     * @return SignalHandler
     */
    public function getSignalHandler()
    {
        if ($this->signalHandler === null) {
            $this->setSignalHandler(new SignalHandler());
        }

        return $this->signalHandler;
    }

    /**
     * @param SignalHandler $signalHandler
     *
     * @return $this
     */
    public function setSignalHandler(SignalHandler $signalHandler)
    {
        $this->signalHandler = $signalHandler;
        return $this;
    }

    /**
     * @return boolean
     */
    public function isShouldShutdown()
    {
        return $this->shouldShutdown;
    }

    /**
     * Execute callable.
     */
    public function run()
    {
        $signalHandler = $this->getSignalHandler();
        $signalHandler->registerHandler(SIGTERM, function () {
            $this->shouldShutdown = true;
        });

        $this->sharedMemory[self::STARTED_MARKER] = true;

        /** @var callable $callable */
        $callable = $this->callable;
        $callable($this);
    }

    /**
     * Return the process ID.
     *
     * @return integer
     */
    public function getPid()
    {
        return $this->pid;
    }

    /**
     * Set the process ID.
     *
     * @param integer $pid
     *
     * @return $this
     */
    public function setPid($pid)
    {
        $this->pid = $pid;

        return $this;
    }

    /**
     * Return the status parameter supplied to a successful call to wait()
     *
     * @return mixed
     */
    public function getStatus()
    {
        return $this->status;
    }

    /**
     * Set status parameter
     *
     * @param int $status
     */
    public function setStatus($status)
    {
        $this->status = $status;
    }

    /**
     * Returns the return code of a terminated child.
     *
     * @return int
     */
    public function getExitCode()
    {
        return $this->exitCode;
    }

    /**
     * Suspends execution of the current process until a child has exited.
     *
     * @return $this
     */
    public function wait()
    {
        $this->internalWait();
        $event = $this->isSuccessExit()
            ? 'success'
            : 'error';

        $this->internalEmit('exit', $this->pid);
        $this->internalEmit($event);

        return $this;
    }

    protected function internalWait()
    {
        if (0 === $this->pid) {
            return;
        }

        pcntl_waitpid($this->pid, $this->status);
    }

    /**
     * Check exit code and return TRUE if process was ended successfully.
     *
     * @return bool
     */
    public function isSuccessExit()
    {
        $this->exitCode = pcntl_wexitstatus($this->status);
        return (pcntl_wifexited($this->status) && ($this->exitCode === 0));
    }

    /**
     * Setup error handler.
     *
     * @param callable $callable
     *
     * @return $this
     */
    public function onError(callable $callable)
    {
        $this->internalOn('error', $callable);
        return $this;
    }

    /**
     * Setup success handler.
     *
     * @param callable $callable
     *
     * @return $this
     */
    public function onSuccess(callable $callable)
    {
        $this->internalOn('success', $callable);
        return $this;
    }

    /**
     * Send a signal to a process.
     *
     * @param int $signal
     *
     * @return $this;
     */
    public function kill($signal = SIGTERM)
    {
        posix_kill($this->pid, $signal);
        return $this;
    }

    /**
     * Calls signal handlers for pending signals.
     *
     * @return $this;
     *
     * @deprecated Use ProcessManager::dispatch()
     */
    public function dispatchSignals()
    {
        return $this->dispatch();
    }

    /**
     * Calls signal handlers for pending signals.
     *
     * @return $this
     */
    public function dispatch()
    {
        $this->getSignalHandler()
            ->dispatch();

        return $this;
    }

    /**
     * Mark process as completely initialized. Any object, which need to handle Process life time should use this
     * flag to build they internal login about process handling.
     *
     * @param bool $value
     *
     * @return $this
     */
    public function setReady($value)
    {
        $this[self::STARTED_MARKER] = $value;
        return $this;
    }

    /**
     * Return True is process is ready.
     *
     * @return bool
     *
     * @see setReady
     */
    public function isReady()
    {
        return (bool)$this[self::STARTED_MARKER];
    }

    /**
     * Wait Process is completely initialized.
     *
     * @return $this;
     *
     * @limitation This method should not to be called from child process.
     * @throw \RuntimeException If time out occurred during wait;
     */
    public function waitReady()
    {
        $x = 0;
        while ($x++ < 100) {
            usleep(self::WAIT_IDLE);
            if ($this[self::STARTED_MARKER] === true) {
                return $this;
            }
        }

        throw new \RuntimeException('Wait process running timeout for child pid ' . $this->getPid());
    }

    /**
     * (PHP 5 >= 5.0.0)

     * Whether a offset exists
     * @link http://php.net/manual/en/arrayaccess.offsetexists.php
     *
     * @param mixed $offset

     * An offset to check for.
     *

     *
     * @return boolean true on success or false on failure.
     *

     *

     * The return value will be casted to boolean if non-boolean was returned.
     */
    public function offsetExists($offset)
    {
        $sm = $this->getSharedMemory();
        return $sm->offsetExists($offset);
    }

    /**
     * (PHP 5 >= 5.0.0)

     * Offset to retrieve
     * @link http://php.net/manual/en/arrayaccess.offsetget.php
     *
     * @param mixed $offset

     * The offset to retrieve.
     *

     *
     * @return mixed Can return all value types.
     */
    public function offsetGet($offset)
    {
        $sm = $this->getSharedMemory();
        return $sm->offsetGet($offset);
    }

    /**
     * (PHP 5 >= 5.0.0)

     * Offset to set
     * @link http://php.net/manual/en/arrayaccess.offsetset.php
     *
     * @param mixed $offset

     * The offset to assign the value to.
     *

     * @param mixed $value

     * The value to set.
     *

     *
     * @return void
     */
    public function offsetSet($offset, $value)
    {
        $sm = $this->getSharedMemory();
        $sm->offsetSet($offset, $value);
    }

    /**
     * (PHP 5 >= 5.0.0)

     * Offset to unset
     * @link http://php.net/manual/en/arrayaccess.offsetunset.php
     *
     * @param mixed $offset

     * The offset to unset.
     *

     *
     * @return void
     */
    public function offsetUnset($offset)
    {
        $sm = $this->getSharedMemory();
        $sm->offsetUnset($offset);
    }

    /**
     * (PHP 5 >= 5.1.0)

     * Count elements of an object
     * @link http://php.net/manual/en/countable.count.php
     * @return int The custom count as an integer.
     *

     *

     * The return value is cast to an integer.
     */
    public function count()
    {
        $sm = $this->getSharedMemory();
        return $sm->count();
    }
}


<?php
/**
 * The MIT License
 *
 * Copyright (c) 2015 Nikolay Bondarenko <misterionkell@gmail.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 * PHP version 5.4
 *
 * @package Ko
 * @author Nikolay Bondarenko
 * @copyright 2015 Nikolay Bondarenko. All rights reserved.
 * @license MIT http://opensource.org/licenses/MIT
 */

namespace Ko;

/**
 * Class Semaphore
 *
 * @package Ko
 * @author Nikolay Bondarenko <misterionkell@gmail.com>
 * @copyright 2015 Nikolay Bondarenko. All rights reserved.
 * @version 1.0
 */
class Semaphore
{
    /**
     * @var resource
     */
    protected $mutex;

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

    /**
     * @var bool
     */
    protected $isAcquired;

    /**
     * Creator process pid
     *
     * @var int
     */
    protected $pid;

    public function __construct($key = null)
    {
        $this->pid = getmypid();

        $this->isAcquired = false;
        if ($key) {
            $semKey = $key;
        } else {
            $this->file = tempnam(sys_get_temp_dir(), 's');
            $semKey = ftok($this->file, 'a');
        }

        $this->mutex = sem_get($semKey, 1); //auto_release = 1 by default
        if (!$this->mutex) {
            throw new \RuntimeException('Unable to create the semaphore');
        }
    }

    public function __destruct()
    {
        if ($this->isAcquired()) {
            $this->release();
        }

        if ($this->pid === getmypid()) {
            $this->remove();
        }
    }

    /**
     * Remove semaphore.
     */
    public function remove()
    {
        if (is_resource($this->mutex)) {
            sem_remove($this->mutex);
        }

        if (file_exists($this->file)) {
            unlink($this->file);
        }
    }

    /**
     * Return TRUE if semaphore acquired in this process.
     *
     * @return boolean
     */
    public function isAcquired()
    {
        return $this->isAcquired;
    }

    /**
     * Release a semaphore.
     *
     * @return bool
     */
    public function release()
    {
        $this->isAcquired = !sem_release($this->mutex);

        return $this->isAcquired;
    }

    /**
     * Lock and execute given callable.
     *
     * @param callable $callable
     *
     * @return mixed
     */
    public function lockExecute(callable $callable)
    {
        $this->isAcquired = $this->acquire();
        $result = $callable();
        $this->isAcquired = $this->release();

        return $result;
    }

    /**
     * Acquire a semaphore.
     *
     * @return bool
     */
    public function acquire()
    {
        $this->isAcquired = sem_acquire($this->mutex);

        return $this->isAcquired;
    }
}


<?php
/**
 * The MIT License
 *
 * Copyright (c) 2015 Nikolay Bondarenko <misterionkell@gmail.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 * PHP version 5.4
 *
 * @package Ko
 * @author Nikolay Bondarenko
 * @copyright 2015 Nikolay Bondarenko. All rights reserved.
 * @license MIT http://opensource.org/licenses/MIT
 */

namespace Ko;

/**
 * Class ProcessManager
 *
 * @package Ko
 * @author Nikolay Bondarenko <misterionkell@gmail.com>
 * @copyright 2015 Nikolay Bondarenko. All rights reserved.
 * @version 1.0
 *
 * @SuppressWarnings(PHPMD.ExitExpression)
 */
class ProcessManager implements \Countable
{
    use Mixin\ProcessTitle;
    use Mixin\EventEmitter {
        on as protected internalOn;
        emit as protected internalEmit;
    }

    /**
     * @var Process[]
     */
    protected $children;

    /**
     * @var Process[]
     */
    protected $spawnWatch;

    /**
     * @var bool
     */
    protected $sigTerm;

    /**
     * @var SignalHandler
     */
    protected $signalHandler;

    public function __construct()
    {
        $this->children = [];
        $this->spawnWatch = [];
        $this->sigTerm = false;

        $this->setupSignalHandlers();
    }

    protected function setupSignalHandlers()
    {
        $this->signalHandler = new SignalHandler();
        $this->signalHandler->registerHandler(SIGCHLD, function() {
            $this->handleSigChild();
        });

        $this->signalHandler->registerHandler(SIGTERM, function() {
            $this->handleSigTerm();
        });
    }

    /**
     * @internal
     */
    public function handleSigChild()
    {
        while (($pid = pcntl_waitpid(-1, $status, WNOHANG)) > 0) {
            $this->childProcessDie($pid, $status);
        }

        //INFO Fix issue https://github.com/misterion/ko-process/pull/15
        gc_collect_cycles();
    }

    /**
     * @internal
     */
    public function handleSigTerm()
    {
        $this->sigTerm = true;

        foreach ($this->children as $process) {
            $process->kill();
        }

        $this->internalEmit('shutdown');
    }

    protected function childProcessDie($pid, $status)
    {
        unset($this->children[$pid]);
        if (!isset($this->spawnWatch[$pid])) {
            return;
        }

        if ($this->sigTerm) {
            unset($this->spawnWatch[$pid]);
            return;
        }

        $p = $this->spawnWatch[$pid];
        unset($this->spawnWatch[$pid]);

        $p->setStatus($status);

        if ($p->isSuccessExit()) {
            return;
        }

        $this->internalSpawn($p);
    }

    protected function internalSpawn(Process $p)
    {
        $p = $this->internalFork($p);
        $this->spawnWatch[$p->getPid()] = $p;

        return $p;
    }

    /**
     * @param Process $p
     *
     * @return Process
     */
    protected function internalFork(Process $p)
    {
        $p->setReady(false);

        $pid = pcntl_fork();
        if (-1 === $pid) {
            throw new \RuntimeException('Failure on pcntl_fork');
        }

        if ($pid) {
            $this->children[$pid] = $p;

            return $p->setPid($pid)
                ->waitReady();
        }

        $p->setPid(getmypid())
            ->run();

        exit(0);
    }

    /**
     * Forks the currently running process.
     *
     * @param callable $callable Callable with prototype like function(Ko\Process $p) {}
     *
     * @return Process
     * @throws \RuntimeException
     */
    public function fork(callable $callable)
    {
        return $this->internalFork($this->createProcess($callable));
    }

    protected function createProcess(callable $callable)
    {
        $p = new Process($callable);
        $p->setSharedMemory(new SharedMemory());
        $p->on('exit', function ($pid) use ($p) {
            $this->childProcessDie($pid, $p->getStatus());
        });

        return $p;
    }

    /**
     * @param callable $callable Callable with prototype like function(Ko\Process $p) {}
     *
     * @return Process
     * @throws \RuntimeException
     */
    public function spawn(callable $callable)
    {
        return $this->internalSpawn($this->createProcess($callable));
    }

    /**
     * Suspends execution of the current process until all children has exited, or until a signal is delivered whose
     * action is to terminate the current process.
     */
    public function wait()
    {
        while ($this->hasAlive()) {
            $this->dispatch();
            usleep(100000);
        }
    }

    /**
     * Return TRUE is manager has alive children processes.
     *
     * @return bool
     */
    public function hasAlive()
    {
        return count($this->children) > 0;
    }

    /**
     * Return count of currently running child process.
     *
     * @return int
     */
    public function getProcessCount()
    {
        return count($this->children);
    }

    /**
     * Return count of currently running child process.
     *
     * @return int
     */
    public function count()
    {
        return count($this->children);
    }

    /**
     * Master process shutdown handler. Shutdown handler called right after the SIGTERM catches by this class.
     *
     * @param callable $callable
     *
     * @return $this
     */
    public function onShutdown(callable $callable)
    {
        $this->internalOn('shutdown', $callable);
        return $this;
    }

    /**
     * Forks the currently running process to detach from console.
     *
     * @return $this
     * @throws \RuntimeException
     */
    public function demonize()
    {
        $pid = pcntl_fork();
        if (-1 === $pid) {
            throw new \RuntimeException('Failure on pcntl_fork');
        }

        if ($pid) {
            exit(0);
        }

        return $this;
    }

    /**
     * Calls signal handlers for pending signals.
     *
     * @return $this;
     *
     * @deprecated Use ProcessManager::dispatch()
     */
    public function dispatchSignals()
    {
        return $this->dispatch();
    }

    /**
     * Calls signal handlers for pending signals.
     *
     * @return $this
     */
    public function dispatch()
    {
        $this->signalHandler->dispatch();
        return $this;
    }
}


<?php
/**
 * The MIT License
 *
 * Copyright (c) 2015 Nikolay Bondarenko <misterionkell@gmail.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 * PHP version 5.4
 *
 * @package Ko
 * @author Nikolay Bondarenko
 * @copyright 2015 Nikolay Bondarenko. All rights reserved.
 * @license MIT http://opensource.org/licenses/MIT
 */

namespace Ko;

/**
 * Class SignalHandler
 *
 * A wrapper under the pcntl `pcntl_signal_dispatch` and `pcntl_signal`. This class solves specific issues then signal
 * handlers are not re-entrant. Though, that the "receiving" signals produced after the call pcntl_signal_dispatch,
 * the callback function in pcntl_signal a signal handler, and if out of it kill the current process and start a new
 * (even through the screen), this process is considered to be caused is of a signal handler and unable to receive
 * other signals, as handlers are not re-entrant (code signal handler to get another or the same signal can not).
 *
 * <example>
 *      $handler = new SignalHandler();
 *      $handler->registerHandler(SIGTERM, function() {
 *          echo 'Hello from SIGTERM';
 *      })->registerHandler(SIGCHLD, function() {
 *          echo 'Hello from SIGCHLD';
 *      });
 *      $handler->dispatch();
 *
 * </example>
 *
 * @package Ko
 * @author Nikolay Bondarenko <misterionkell@gmail.com>
 * @copyright 2015 Nikolay Bondarenko. All rights reserved.
 * @version 1.0
 * @since 0.4
 */
class SignalHandler implements \Countable
{
    /**
     * @var []
     */
    private $handlers;

    /**
     * @var \SplQueue
     */
    private $signalQueue;

    public function __construct()
    {
        $this->handlers = [];

        $this->signalQueue = new \SplQueue();
        $this->signalQueue->setIteratorMode(\SplQueue::IT_MODE_DELETE);
    }

    /**
     * Register given callable with pcntl signal.
     *
     * @param int $signal The pcntl signal.
     * @param callback $handler The signal handler.
     *
     * @return $this
     * @throws \RuntimeException If could not register handler with pcntl_signal.
     */
    public function registerHandler($signal, $handler)
    {
        if (!is_callable($handler)) {
            throw new \InvalidArgumentException('The handler is not callable');
        }

        if (!isset($this->handlers[$signal])) {
            $this->handlers[$signal] = [];

            if (!pcntl_signal($signal, [$this, 'handleSignal'])) {
                throw new \RuntimeException(sprintf('Could not register signal %d with pcntl_signal', $signal));
            };
        };

        $this->handlers[$signal][] = $handler;
        return $this;
    }

    /**
     * Return true is ant handler registered with given signal.
     *
     * @param int $signal The pcntl signal.
     *
     * @return bool
     */
    public function hasHandler($signal)
    {
        return !empty($this->handlers[$signal]);
    }

    /**
     * Enqueue pcntl signal to dispatch in feature.
     *
     * @param int $signal The pcntl signal.
     *
     * @return void
     */
    public function handleSignal($signal)
    {
        $this->signalQueue->enqueue($signal);
    }

    /**
     * Execute `pcntl_signal_dispatch` and process all registered handlers.
     *
     * @return $this
     */
    public function dispatch()
    {
        pcntl_signal_dispatch();

        foreach ($this->signalQueue as $signal) {
            foreach ($this->handlers[$signal] as &$callable) {
                call_user_func($callable, $signal);
            }
        }

        return $this;
    }

    /**
     * Return count of queued signals.
     *
     * @link http://php.net/manual/en/countable.count.php
     * @return int The custom count as an integer.
     * </p>
     * <p>
     * The return value is cast to an integer.
     */
    public function count()
    {
        return count($this->signalQueue);
    }
}


<?php
/**
 * The MIT License
 *
 * Copyright (c) 2015 Nikolay Bondarenko <misterionkell@gmail.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 * PHP version 5.4
 *
 * @package Ko
 * @author Nikolay Bondarenko
 * @copyright 2015 Nikolay Bondarenko. All rights reserved.
 * @license MIT http://opensource.org/licenses/MIT
 */

namespace Ko;

/**
 * Class SharedMemory
 *
 * Php shared memory functions wrapper. You may use this class as array like structure above shared memory.
 *
 * @package Ko
 * @author Nikolay Bondarenko <misterionkell@gmail.com>
 * @copyright 2015 Nikolay Bondarenko. All rights reserved.
 * @version 1.0
 */
class SharedMemory implements \ArrayAccess, \Countable
{
    /**
     * @var resource
     */
    public $id;

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

    /**
     * @var int
     */
    protected $size;

    /**
     * @var Semaphore
     */
    protected $mutex;

    /**
     * Creator process pid
     *
     * @var int
     */
    protected $pid;

    public function __construct($memorySize = 1024)
    {
        $this->pid = getmypid();

        $this->size = $memorySize;

        $this->file = tempnam(sys_get_temp_dir(), 's');
        $key = ftok($this->file, 'a');

        $this->id = shm_attach($key, $this->size);
        if (false === $this->id) {
            throw new \RuntimeException('Unable to create the shared memory segment');
        }

        //Keymapper (maps mixed to integers)
        shm_put_var($this->id, 0, []);
        $this->mutex = new Semaphore($key);
    }

    public function __destruct()
    {
        if ($this->pid === getmypid()) {
            unset($this->mutex); //Destroy mutex first because of we share same System V IPC key.
            $this->remove();
        }
    }

    public function remove()
    {
        if (is_resource($this->id)) {
            shm_remove($this->id);
        }

        if (file_exists($this->file)) {
            unlink($this->file);
        }
    }

    /**
     * (PHP 5 >= 5.0.0)

     * Whether a offset exists
     * @link http://php.net/manual/en/arrayaccess.offsetexists.php
     *
     * @param mixed $offset

     * An offset to check for.
     *

     *
     * @return boolean true on success or false on failure.
     *

     *

     * The return value will be casted to boolean if non-boolean was returned.
     */
    public function offsetExists($offset)
    {
        $task = function() use ($offset) {
            return shm_has_var($this->id, $this->getKey($offset));
        };

        return $this->mutex->lockExecute($task);
    }

    public function getKeys()
    {
        return array_keys($this->getKeyMap());
    }

    protected function getKeyMap()
    {
        $task = function() {
            return shm_get_var($this->id, 0);
        };

        return $this->lockExecute($task);
    }

    protected function getKey($offset)
    {
        $keyMapper = $this->getKeyMap();
        if (isset($keyMapper[$offset])) {
            return $keyMapper[$offset];
        }

        //Get the next available integer for shm
        if(count($keyMapper) > 0) {
            $keyMapper[$offset] = max($keyMapper)+1;
        }
        else {
            $keyMapper[$offset] = 1;
        }
        shm_put_var($this->id, 0, $keyMapper);
        return $keyMapper[$offset];
    }

    /**
     * (PHP 5 >= 5.0.0)

     * Offset to retrieve
     * @link http://php.net/manual/en/arrayaccess.offsetget.php
     *
     * @param mixed $offset

     * The offset to retrieve.
     *

     *
     * @return mixed Can return all value types.
     */
    public function offsetGet($offset)
    {
        $task = function() use ($offset) {
            $key = $this->getKey($offset);
            if(shm_has_var($this->id, $key)) {
                return shm_get_var($this->id, $key);
            }

            return null;
        };

        return $this->lockExecute($task);
    }

    /**
     * (PHP 5 >= 5.0.0)

     * Offset to set
     * @link http://php.net/manual/en/arrayaccess.offsetset.php
     *
     * @param mixed $offset

     * The offset to assign the value to.
     *

     * @param mixed $value

     * The value to set.
     *

     *
     * @return void
     */
    public function offsetSet($offset, $value)
    {
        $task = function() use ($offset,$value) {
            shm_put_var($this->id, $this->getKey($offset), $value);
        };

        $this->lockExecute($task);
    }

    /**
     * (PHP 5 >= 5.0.0)

     * Offset to unset
     * @link http://php.net/manual/en/arrayaccess.offsetunset.php
     *
     * @param mixed $offset

     * The offset to unset.
     *

     *
     * @return void
     */
    public function offsetUnset($offset)
    {
        $task = function() use ($offset) {
            $keyMapper = $this->getKeyMap();
            if(array_key_exists($offset,$keyMapper)) {
                shm_remove_var($this->id, $keyMapper[$offset]);
                unset($keyMapper[$offset]);
                shm_put_var($this->id, 0, $keyMapper);
            }
        };

        $this->lockExecute($task);
    }

    /**
     * (PHP 5 >= 5.1.0)

     * Count elements of an object
     * @link http://php.net/manual/en/countable.count.php
     * @return int The custom count as an integer.
     *

     *

     * The return value is cast to an integer.
     */
    public function count()
    {
        $task = function() {
            return count($this->getKeyMap());
        };

        return $this->lockExecute($task);
    }

    /**
     * Lock shared memory if lock not already acquired by this process
     *
     * @param callable $task Function to execute
     *
     * @return mixed    Whatever the $task function returns
     */
    protected function lockExecute(callable $task)
    {
        if($this->mutex->isAcquired()) {
            return $task();
        }

        return $this->mutex->lockExecute($task);
    }

    /**
     * Lock shared memory
     *
     * @return boolean    True if locked, otherwise false
     */
    public function lock()
    {
        return $this->mutex->acquire();
    }

    /**
     * Unlock shared memory
     *
     * @return boolean    True if unlocked, otherwise false
     */
    public function release()
    {
        return $this->mutex->release();
    }
}


<?php
/**
 * Twig Profiler plugin for Craft CMS 3.x
 *
 * Twig Profiler allows you to profile sections of your Twig templates, and see
 * the resulting timings in the Yii2 Debug Toolbar
 *
 * @link      https://nystudio107.com/
 * @copyright Copyright (c) 2018 nystudio107
 */

namespace nystudio107\twigprofiler\services;

use Craft;
use craft\base\Component;
use nystudio107\twigprofiler\TwigProfiler;

/**
 * @author    nystudio107
 * @package   TwigProfiler
 * @since     1.0.0
 */
class Profile extends Component
{
    // Constants
    // =========================================================================

    const CATEGORY_PREFIX = 'Twig Profiler';

    // Public Methods
    // =========================================================================

    /**
     * Start logging profiling data
     *
     * @param string $profile
     */
    public function begin(string $profile)
    {
        Craft::beginProfile(
            $profile,
            Craft::t('twig-profiler', self::CATEGORY_PREFIX)
            .TwigProfiler::$renderingTemplate
        );
    }

    /**
     * End logging profiling data
     *
     * @param string $profile
     */
    public function end(string $profile)
    {
        Craft::endProfile(
            $profile,
            Craft::t('twig-profiler', self::CATEGORY_PREFIX)
            .TwigProfiler::$renderingTemplate
        );
    }
}


<?php

namespace Probe\Provider;

/**
 * Linux information provider
 * @author Eugene Terentev <eugene@terentev.net>
 */
class LinuxProvider extends AbstractUnixProvider
{
    /**
     * @var array|null
     */
    protected $cpuInfo;
    /**
     * @var
     */
    protected $cpuInfoByLsCpu;

    /**
     * @inheritdoc
     */
    public function getUptime()
    {
        $uptime = file_get_contents('/proc/uptime');
        $uptime = explode('.', $uptime);
        return (int) array_shift($uptime);
    }

    /**
     * @inheritdoc
     */
    public function getOsRelease()
    {
        return shell_exec('/usr/bin/lsb_release -ds');
    }

    /**
     * @inheritdoc
     */
    public function getOsKernelVersion()
    {
        return shell_exec('/bin/uname -r');
    }

    /**
     * @inheritdoc
     */
    public function getTotalSwap()
    {
        $meminfo = $this->getMemInfo();
        return array_key_exists('SwapTotal', $meminfo) ? intval($meminfo['SwapTotal']) * 1024 : null;
    }

    /**
     * @inheritdoc
     */
    public function getFreeSwap()
    {
        $memInfo = $this->getMemInfo();
        return array_key_exists('SwapFree', $memInfo) ? intval($memInfo['SwapFree']) * 1024 : null;
    }

    /**
     * @return int|null
     */
    public function getUsedSwap()
    {
        return $this->getTotalSwap() - $this->getFreeSwap();
    }

    /**
     * @inheritdoc
     */
    public function getTotalMem()
    {
        $meminfo = $this->getMemInfo();
        return array_key_exists('MemTotal', $meminfo) ? intval($meminfo['MemTotal']) * 1024 : null;
    }

    /**
     * @inheritdoc
     */
    public function getFreeMem()
    {
        $memInfo = $this->getMemInfo();

        $memFree = array_key_exists('MemFree', $memInfo) ? (int) $memInfo['MemFree'] : null;
        $cached  = array_key_exists('Cached', $memInfo) ? (int) $memInfo['Cached'] : null;

        $result = ($memFree ?: null) + ($cached ?: null);

        return $result ? $result * 1024: null;
    }

    /**
     * @inheritdoc
     */
    public function getUsedMem()
    {
        return $this->getTotalMem() - $this->getFreeMem();
    }

    /**
     * @inheritdoc
     */
    public function getOsType()
    {
        return 'Linux';
    }

    /**
     * @inheritdoc
     */
    public function getCpuinfo()
    {
        if (!$this->cpuInfo) {
            $cpuInfo = file_get_contents('/proc/cpuinfo');
            $cpuInfo = explode("\n", $cpuInfo);
            $values = [];
            foreach ($cpuInfo as $v) {
                $v = array_map('trim', explode(':', $v));
                if (isset($v[0], $v[1])) {
                    $values[$v[0]] = $v[1];
                }
            }
            $this->cpuInfo = $values;
        }
        return $this->cpuInfo;
    }

    /**
     * Get information about CPU using lscpu untility
     * @return array
     */
    public function getCpuinfoByLsCpu()
    {
        if (!$this->cpuInfoByLsCpu) {
            $lscpu = shell_exec('lscpu');
            $lscpu = explode("\n", $lscpu);
            $values = [];
            foreach ($lscpu as $v) {
                $v = array_map('trim', explode(':', $v));
                if (isset($v[0], $v[1])) {
                    $values[$v[0]] = $v[1];
                }
            }
            $this->cpuInfoByLsCpu = $values;
        }
        return $this->cpuInfoByLsCpu;
    }

    /**
     * @inheritdoc
     */
    public function getCpuModel()
    {
        $cu = $this->getCpuinfo();
        return array_key_exists('model name', $cu) ? $cu['model name'] : null;
    }

    /**
     * @inheritdoc
     */
    public function getCpuVendor()
    {
        $cu = $this->getCpuinfo();
        return array_key_exists('vendor_id', $cu) ? $cu['vendor_id'] : null;
    }

    /**
     * Return number of physical CPUs
     * @return mixed|null
     */
    public function getPhysicalCpus()
    {
        $cu = $this->getCpuinfoByLsCpu();
        return array_key_exists('CPU(s)', $cu) ? $cu['CPU(s)'] : null;
    }

    /**
     * @return mixed|null
     */
    public function getCoresPerSocket()
    {
        $cu = $this->getCpuinfoByLsCpu();
        return array_key_exists('Core(s) per socket', $cu) ? $cu['Core(s) per socket'] : null;
    }

    /**
     * @inheritdoc
     */
    public function getCpuCores()
    {
        $cu = $this->getCpuinfo();
        return array_key_exists('siblings', $cu) ? $cu['siblings'] : null;
    }

    /**
     * @inheritdoc
     */
    public function getCpuPhysicalCores()
    {
        $cu = $this->getCpuinfo();
        return array_key_exists('cpu cores', $cu) ? $cu['cpu cores'] : null;
    }

    /**
     * @inheritdoc
     */
    public function getDiskUsage()
    {
        return $this->getDiskUsageInfo();
    }

    /**
     * @inheritdoc
     */
    public function getDiskTotal()
    {
        $du = $this->getDiskUsageInfo();
        return array_key_exists('-', $du) ? $du['-']['size'] : null;
    }

    /**
     * @inheritdoc
     */
    public function getDiskFree()
    {
        $du = $this->getDiskUsageInfo();
        return array_key_exists('-', $du) ? $du['-']['avail'] : null;
    }
}


<?php

namespace Amp\Parser;

class Parser {
    /** @var \Generator */
    private $generator;

    /** @var string */
    private $buffer = '';

    /** @var int|string|null */
    private $delimiter;

    /**
     * @param \Generator $generator
     *
     * @throws InvalidDelimiterError If the generator yields an invalid delimiter.
     * @throws \Throwable If the generator throws.
     */
    public function __construct(\Generator $generator) {
        $this->generator = $generator;

        $this->delimiter = $this->generator->current();

        if (!$this->generator->valid()) {
            $this->generator = null;
            return;
        }

        if ($this->delimiter !== null
            && (!\is_int($this->delimiter) || $this->delimiter <= 0)
            && (!\is_string($this->delimiter) || !\strlen($this->delimiter))
        ) {
            throw new InvalidDelimiterError(
                $generator,
                \sprintf(
                    "Invalid value yielded: Expected NULL, an int greater than 0, or a non-empty string; %s given",
                    \is_object($this->delimiter) ? \sprintf("instance of %s", \get_class($this->delimiter)) : \gettype($this->delimiter)
                )
            );
        }
    }

    /**
     * Cancels the generator parser and returns any remaining data in the internal buffer. Writing data after calling
     * this method will result in an error.
     *
     * @return string
     */
    final public function cancel(): string {
        $this->generator = null;
        return $this->buffer;
    }

    /**
     * @return bool True if the parser can still receive more data to parse, false if it has ended and calling push
     *     will throw an exception.
     */
    final public function isValid(): bool {
        return $this->generator !== null;
    }

    /**
     * Adds data to the internal buffer and tries to continue parsing.
     *
     * @param string $data Data to append to the internal buffer.
     *
     * @throws InvalidDelimiterError If the generator yields an invalid delimiter.
     * @throws \Error If parsing has already been cancelled.
     * @throws \Throwable If the generator throws.
     */
    final public function push(string $data) {
        if ($this->generator === null) {
            throw new \Error("The parser is no longer writable");
        }

        $this->buffer .= $data;
        $end = false;

        try {
            while ($this->buffer !== "") {
                if (\is_int($this->delimiter)) {
                    if (\strlen($this->buffer) < $this->delimiter) {
                        break; // Too few bytes in buffer.
                    }

                    $send = \substr($this->buffer, 0, $this->delimiter);
                    $this->buffer = \substr($this->buffer, $this->delimiter);
                } elseif (\is_string($this->delimiter)) {
                    if (($position = \strpos($this->buffer, $this->delimiter)) === false) {
                        break;
                    }

                    $send = \substr($this->buffer, 0, $position);
                    $this->buffer = \substr($this->buffer, $position + \strlen($this->delimiter));
                } else {
                    $send = $this->buffer;
                    $this->buffer = "";
                }

                $this->delimiter = $this->generator->send($send);

                if (!$this->generator->valid()) {
                    $end = true;
                    break;
                }

                if ($this->delimiter !== null
                    && (!\is_int($this->delimiter) || $this->delimiter <= 0)
                    && (!\is_string($this->delimiter) || !\strlen($this->delimiter))
                ) {
                    throw new InvalidDelimiterError(
                        $this->generator,
                        \sprintf(
                            "Invalid value yielded: Expected NULL, an int greater than 0, or a non-empty string; %s given",
                            \is_object($this->delimiter) ? \sprintf("instance of %s", \get_class($this->delimiter)) : \gettype($this->delimiter)
                        )
                    );
                }
            }
        } catch (\Throwable $exception) {
            $end = true;
            throw $exception;
        } finally {
            if ($end) {
                $this->generator = null;
            }
        }
    }
}


<?php

namespace Chefkoch\Morphoji;

/**
 * Class Detector
 *
 * Offers the unicode codes of emoji characters as well as a regex pattern based
 * on them. Can also extract all emoji characters from a given string.
 *
 * @package Chefkoch\Morphoji
 */
class Detector
{

    /** @var string */
    private static $emojiPattern;

    /**
     * @param string $string
     * @return array
     */
    public function getEmoji($string)
    {
        preg_match_all($this->getEmojiPattern(), $string, $matches);

        return isset($matches[0]) ? array_pop($matches) : [];
    }

    /**
     * Returns a regular expression pattern to detect emoji characters.
     *
     * @return string
     */
    public function getEmojiPattern()
    {
        if (null === self::$emojiPattern) {
            $codeString = '';

            foreach ($this->getEmojiCodeList() as $code) {
                if (is_array($code)) {

                    $first = dechex(array_shift($code));
                    $last  = dechex(array_pop($code));
                    $codeString .= '\x{' . $first . '}-\x{' . $last . '}';

                } else {
                    $codeString .= '\x{' . dechex($code) . '}';
                }
            }

            self::$emojiPattern = "/[$codeString]/u";
        }

        return self::$emojiPattern;
    }

    /**
     * Returns an array with all unicode values for emoji characters.
     *
     * This is a function so the array can be defined with a mix of hex values
     * and range() calls to conveniently maintain the array with information
     * from the official Unicode tables (where values are given in hex as well).
     *
     * With PHP > 5.6 this could be done in class variable, maybe even a
     * constant.
     *
     * @return array
     */
    public function getEmojiCodeList()
    {
        return [
            // Various 'older' charactes, dingbats etc. which over time have
            // received an additional emoji representation.
            0x203c,
            0x2049,
            0x2122,
            0x2139,
            range(0x2194, 0x2199),
            range(0x21a9, 0x21aa),
            range(0x231a, 0x231b),
            0x2328,
            range(0x23ce, 0x23cf),
            range(0x23e9, 0x23f3),
            range(0x23f8, 0x23fa),
            0x24c2,
            range(0x25aa, 0x25ab),
            0x25b6,
            0x25c0,
            range(0x25fb, 0x25fe),
            range(0x2600, 0x2604),
            0x260e,
            0x2611,
            range(0x2614, 0x2615),
            0x2618,
            0x261d,
            0x2620,
            range(0x2622, 0x2623),
            0x2626,
            0x262a,
            range(0x262e, 0x262f),
            range(0x2638, 0x263a),
            0x2640,
            0x2642,
            range(0x2648, 0x2653),
            0x2660,
            0x2663,
            range(0x2665, 0x2666),
            0x2668,
            0x267b,
            0x267f,
            range(0x2692, 0x2697),
            0x2699,
            range(0x269b, 0x269c),
            range(0x26a0, 0x26a1),
            range(0x26aa, 0x26ab),
            range(0x26b0, 0x26b1),
            range(0x26bd, 0x26be),
            range(0x26c4, 0x26c5),
            0x26c8,
            range(0x26ce, 0x26cf),
            0x26d1,
            range(0x26d3, 0x26d4),
            range(0x26e9, 0x26ea),
            range(0x26f0, 0x26f5),
            range(0x26f7, 0x26fa),
            0x26fd,
            0x2702,
            0x2705,
            range(0x2708, 0x270d),
            0x270f,
            0x2712,
            0x2714,
            0x2716,
            0x271d,
            0x2721,
            0x2728,
            range(0x2733, 0x2734),
            0x2744,
            0x2747,
            0x274c,
            0x274e,
            range(0x2753, 0x2755),
            0x2757,
            range(0x2763, 0x2764),
            range(0x2795, 0x2797),
            0x27a1,
            0x27b0,
            0x27bf,
            range(0x2934, 0x2935),
            range(0x2b05, 0x2b07),
            range(0x2b1b, 0x2b1c),
            0x2b50,
            0x2b55,
            0x3030,
            0x303d,
            0x3297,
            0x3299,

            // Modifier for emoji sequences.
            0x200d,
            0x20e3,
            0xfe0f,

            // 'Regular' emoji unicode space, containing the bulk of them.
            range(0x1f000, 0x1f9cf)
        ];
    }
}