My first commit of codes

This commit is contained in:
sujitprasad
2015-05-01 13:13:01 +05:30
parent 4c8e5096f1
commit a6e5a69348
8487 changed files with 1317246 additions and 0 deletions

View File

@@ -0,0 +1,8 @@
preset: recommended
enabled:
- concat_with_spaces
disabled:
- concat_without_spaces
- short_array_syntax

View File

@@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2013-2014 Michael Dowling <mtdowling@gmail.com>
Copyright (c) 2014-2015 Graham Campbell <graham@mineuk.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.

View File

@@ -0,0 +1,11 @@
#! /usr/bin/env php
<?php
if (file_exists($autoloadPath = __DIR__ . '/../../autoload.php')) {
require_once $autoloadPath;
} else {
require_once __DIR__ . '/vendor/autoload.php';
}
$application = new ClassPreloader\Application();
$application->run();

View File

@@ -0,0 +1,40 @@
{
"name": "classpreloader/classpreloader",
"description": "Helps class loading performance by generating a single PHP file containing all of the autoloaded files for a specific use case",
"keywords": ["autoload", "class", "preload"],
"license": "MIT",
"authors": [
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com"
},
{
"name": "Graham Campbell",
"email": "graham@mineuk.com"
}
],
"require":{
"php": ">=5.3.3",
"symfony/console": "~2.1",
"symfony/filesystem": "~2.1",
"symfony/finder": "~2.1",
"nikic/php-parser": "^1.2.2"
},
"require-dev":{
"phpunit/phpunit": "~4.0"
},
"autoload": {
"psr-4": {
"ClassPreloader\\": "src/"
}
},
"autoload-dev": {
"classmap": ["tests/stubs/"]
},
"bin": ["classpreloader.php"],
"extra": {
"branch-alias": {
"dev-master": "1.3-dev"
}
}
}

View File

@@ -0,0 +1,40 @@
<?php
namespace ClassPreloader;
use Symfony\Component\Console\Application as BaseApplication;
use Symfony\Component\Finder\Finder;
/**
* This is the application class.
*
* This is sets everything up for the CLI.
*/
class Application extends BaseApplication
{
/**
* Create a new application.
*
* @return void
*/
public function __construct()
{
parent::__construct('Class Preloader', '1.2');
// Create a finder to find each non-abstract command in the filesystem
$finder = new Finder();
$finder->files()
->in(__DIR__ . '/Command')
->notName('Abstract*')
->name('*.php');
// Add each command to the CLI
foreach ($finder as $file) {
$filename = str_replace('\\', '/', $file->getRealpath());
$pos = strripos($filename, '/ClassPreloader/') + strlen('/ClassPreloader/src/');
$class = __NAMESPACE__ . '\\'
. substr(str_replace('/', '\\', substr($filename, $pos)), 0, -4);
$this->add(new $class());
}
}
}

View File

@@ -0,0 +1,105 @@
<?php
namespace ClassPreloader;
/**
* This is the class list class.
*
* This maintains a list of classes using a sort of doubly-linked list.
*/
class ClassList
{
/**
* The head node of the list.
*
* @var \ClassPreloader\ClassNode
*/
protected $head;
/**
* The current node of the list.
*
* @var \ClassPreloader\ClassNode
*/
protected $current;
/**
* Create a new class list.
*
* @return void
*/
public function __construct()
{
$this->clear();
}
/**
* Clear the contents of the list and reset the head node and current node.
*
* @return void
*/
public function clear()
{
$this->head = new ClassNode();
$this->current = $this->head;
}
/**
* Traverse to the next node in the list.
*
* @return void
*/
public function next()
{
if (isset($this->current->next)) {
$this->current = $this->current->next;
} else {
$this->current->next = new ClassNode(null, $this->current);
$this->current = $this->current->next;
}
}
/**
* Insert a value at the current position in the list.
*
* Any currently set value at this position will be pushed back in the list
* after the new value.
*
* @param mixed $value
*
* @return void
*/
public function push($value)
{
if (!$this->current->value) {
$this->current->value = $value;
} else {
$temp = $this->current;
$this->current = new ClassNode($value, $temp->prev);
$this->current->next = $temp;
$temp->prev = $this->current;
if ($temp === $this->head) {
$this->head = $this->current;
} else {
$this->current->prev->next = $this->current;
}
}
}
/**
* Traverse the ClassList and return a list of classes.
*
* @return array
*/
public function getClasses()
{
$classes = array();
$current = $this->head;
while ($current && $current->value) {
$classes[] = $current->value;
$current = $current->next;
}
return array_filter($classes);
}
}

View File

@@ -0,0 +1,151 @@
<?php
namespace ClassPreloader;
require_once __DIR__ . '/ClassNode.php';
require_once __DIR__ . '/ClassList.php';
/**
* This is the class loader class.
*
* This creates an autoloader that intercepts and keeps track of each include
* in order that files must be included. This autoloader proxies to all other
* underlying autoloaders.
*/
class ClassLoader
{
/**
* The list of loaded classes.
*
* @var \ClassPreloader\ClassList
*/
public $classList;
/**
* Create a new class loader.
*
* @return void
*/
public function __construct()
{
$this->classList = new ClassList();
}
/**
* Destroy the class loader.
*
* This makes sure we're unregistered from the autoloader.
*
* @return void
*/
public function __destruct()
{
$this->unregister();
}
/**
* Wrap a block of code in the autoloader and get a list of loaded classes.
*
* @param callable $func
*
* @return \ClassPreloader\Config
*/
public static function getIncludes($func)
{
$loader = new static();
call_user_func($func, $loader);
$loader->unregister();
$config = new Config();
foreach ($loader->getFilenames() as $file) {
$config->addFile($file);
}
return $config;
}
/**
* Registers this instance as an autoloader.
*
* @return void
*/
public function register()
{
spl_autoload_register(array($this, 'loadClass'), true, true);
}
/**
* Unregisters this instance as an autoloader.
*
* @return void
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
}
/**
* Loads the given class, interface or trait.
*
* We'll return true if it was loaded.
*
* @param string $class
*
* @return bool|null
*/
public function loadClass($class)
{
foreach (spl_autoload_functions() as $func) {
if (is_array($func) && $func[0] === $this) {
continue;
}
$this->classList->push($class);
if (call_user_func($func, $class)) {
break;
}
}
$this->classList->next();
return true;
}
/**
* Get an array of loaded file names in order of loading.
*
* @return array
*/
public function getFilenames()
{
$files = array();
foreach ($this->classList->getClasses() as $class) {
// Push interfaces before classes if not already loaded
try {
$r = new \ReflectionClass($class);
foreach ($r->getInterfaces() as $inf) {
$name = $inf->getFileName();
if ($name && !in_array($name, $files)) {
$files[] = $name;
}
}
if (!in_array($r->getFileName(), $files)) {
$files[] = $r->getFileName();
}
} catch (\ReflectionException $e) {
// We ignore all exceptions related to reflection,
// because in some cases class can't exists. This
// can be if you use in your code constructions like
//
// if (class_exists('SomeClass')) { // <-- here will trigger autoload
// class SomeSuperClass extends SomeClass {
// }
// }
//
// We ignore all problems with classes, interfaces and
// traits.
}
}
return $files;
}
}

View File

@@ -0,0 +1,46 @@
<?php
namespace ClassPreloader;
/**
* This is the class node class.
*
* This class contains a value, and the previous/next pointers.
*/
class ClassNode
{
/**
* The next node pointer.
*
* @var \ClassPreloader\ClassNode|null
*/
public $next;
/**
* The previous node pointer.
*
* @var \ClassPreloader\ClassNode|null
*/
public $prev;
/**
* The value of the class node.
*
* @var mixed
*/
public $value;
/**
* Create a new class node.
*
* @param mixed $value
* @param \ClassPreloader\ClassNode|null $prev
*
* @return void
*/
public function __construct($value = null, $prev = null)
{
$this->value = $value;
$this->prev = $prev;
}
}

View File

@@ -0,0 +1,284 @@
<?php
namespace ClassPreloader\Command;
use ClassPreloader\Config;
use ClassPreloader\Exception\SkipFileException;
use ClassPreloader\Parser\DirVisitor;
use ClassPreloader\Parser\FileVisitor;
use ClassPreloader\Parser\NodeTraverser;
use PhpParser\Lexer;
use PhpParser\Parser;
use PhpParser\PrettyPrinter\Standard as PrettyPrinter;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Filesystem\Filesystem;
/**
* This is the pre-compile command class.
*
* This allows the user to communicate with class preloader.
*/
class PreCompileCommand extends Command
{
/**
* The printer.
*
* @var \PhpParser\PrettyPrinter\Standard
*/
protected $printer;
/**
* The parser.
*
* @var \PhpParser\Parser
*/
protected $parser;
/**
* The input.
*
* @var \Symfony\Component\Console\Input\InputInterface|null
*/
protected $input;
/**
* The output.
*
* @var \Symfony\Component\Console\Output\OutputInterface|null
*/
protected $output;
/**
* The traverser.
*
* @var \ClassPreloader\Parser\NodeTraverser|null
*/
protected $traverser;
/**
* Create a new pre-compile command.
*
* @return void
*/
public function __construct()
{
parent::__construct();
$this->printer = new PrettyPrinter();
$this->parser = new Parser(new Lexer());
}
/**
* Configure the current command.
*
* @return void
*/
protected function configure()
{
parent::configure();
$this->setName('compile')
->setDescription('Compiles classes into a single file')
->addOption('config', null, InputOption::VALUE_REQUIRED, 'CSV of filenames to load, or the path to a PHP script that returns an array of file names')
->addOption('output', null, InputOption::VALUE_REQUIRED)
->addOption('skip_dir_file', null, InputOption::VALUE_NONE, 'Skip files with __DIR__ or __FILE__ to make the cache portable')
->addOption('fix_dir', null, InputOption::VALUE_REQUIRED, 'Convert __DIR__ constants to the original directory of a file', 1)
->addOption('fix_file', null, InputOption::VALUE_REQUIRED, 'Convert __FILE__ constants to the original path of a file', 1)
->addOption('strip_comments', null, InputOption::VALUE_REQUIRED, 'Set to 1 to strip comments from each source file', 0)
->setHelp(<<<EOF
The <info>%command.name%</info> command iterates over each script, normalizes
the file to be wrapped in namespaces, and combines each file into a single PHP
file.
EOF
);
}
/**
* Get the node traverser used by the command.
*
* @return \ClassPreloader\Parser\NodeTraverser
*/
protected function getTraverser()
{
if (!$this->traverser) {
$this->traverser = new NodeTraverser();
if ($this->input->getOption('fix_dir')) {
$this->traverser->addVisitor(new DirVisitor($this->input->getOption('skip_dir_file')));
}
if ($this->input->getOption('fix_file')) {
$this->traverser->addVisitor(new FileVisitor($this->input->getOption('skip_dir_file')));
}
}
return $this->traverser;
}
/**
* Get a pretty printed string of code from a file while applying visitors.
*
* @param string $file
*
* @throws \RuntimeException
*
* @return string
*/
protected function getCode($file)
{
if (!is_readable($file)) {
throw new \RuntimeException("Cannot open {$file} for reading");
}
if ($this->input->getOption('strip_comments')) {
$content = php_strip_whitespace($file);
} else {
$content = file_get_contents($file);
}
$parsed = $this->parser->parse($content);
$stmts = $this->getTraverser()->traverseFile($parsed, $file);
$pretty = $this->printer->prettyPrint($stmts);
// Remove the open PHP tag
if (substr($pretty, 5) === '<?php') {
$pretty = substr($pretty, 7);
}
// Add a wrapping namespace if needed
if (strpos($pretty, 'namespace ') === false) {
$pretty = "namespace {\n" . $pretty . "\n}\n";
}
return $pretty;
}
/**
* Validate the command options.
*
* @throws \InvalidArgumentException
*
* @return void
*/
protected function validateCommand()
{
if (!$this->input->getOption('output')) {
throw new \InvalidArgumentException('An output option is required');
}
if (!$this->input->getOption('config')) {
throw new \InvalidArgumentException('A config option is required');
}
}
/**
* Get a list of files in order.
*
* @param mixed $config Configuration option
*
* @throws \InvalidArgumentException
*
* @return array
*/
protected function getFileList($config)
{
$this->output->writeln('> Loading configuration file');
$filesystem = new Filesystem();
if (strpos($config, ',')) {
return array_filter(explode(',', $config));
}
// Ensure absolute paths are resolved
if (!$filesystem->isAbsolutePath($config)) {
$config = getcwd() . '/' . $config;
}
// Ensure that the config file exists
if (!file_exists($config)) {
throw new \InvalidArgumentException(sprintf('Configuration file "%s" does not exist.', $config));
}
$result = require $config;
if ($result instanceof Config) {
foreach ($result->getVisitors() as $visitor) {
$this->getTraverser()->addVisitor($visitor);
}
return $result;
} elseif (is_array($result)) {
return $result;
}
throw new \InvalidArgumentException('Config must return an array of filenames or a Config object');
}
/**
* Prepare the output file and directory.
*
* @param string $outputFile
*
* @throws \RuntimeException
*
* @return void
*/
protected function prepareOutput($outputFile)
{
$dir = dirname($outputFile);
if (!is_dir($dir) && !mkdir($dir, 0777, true)) {
throw new \RuntimeException('Unable to create directory ' . $dir);
}
}
/**
* Executes the pre-compile command.
*
* @param \Symfony\Component\Console\Input\InputInterface $input
* @param \Symfony\Component\Console\Output\OutputInterface $output
*
* @throws \RuntimeException
*
* @return null|int
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$this->input = $input;
$this->output = $output;
$this->validateCommand();
$outputFile = $this->input->getOption('output');
$config = $this->input->getOption('config');
$files = $this->getFileList($config);
$output->writeLn('- Found ' . count($files) . ' files');
// Make sure that the output dir can be used or create it
$this->prepareOutput($outputFile);
if (!$handle = fopen($input->getOption('output'), 'w')) {
throw new \RuntimeException("Unable to open {$outputFile} for writing");
}
// Write the first line of the output
fwrite($handle, "<?php\n");
$output->writeln('> Compiling classes');
$count = 0;
$countSkipped = 0;
foreach ($files as $file) {
$count++;
try {
$code = $this->getCode($file);
$this->output->writeln('- Writing ' . $file);
fwrite($handle, $code . "\n");
} catch (SkipFileException $ex) {
$countSkipped++;
$this->output->writeln('- Skipping ' . $file);
}
}
fclose($handle);
$output->writeln("> Compiled loader written to {$outputFile}");
$output->writeln('- Files: ' . ($count - $countSkipped) . '/' . $count . ' (skipped: ' . $countSkipped . ')');
$output->writeln('- Filesize: ' . (round(filesize($outputFile) / 1024)) . ' kb');
}
}

View File

@@ -0,0 +1,148 @@
<?php
namespace ClassPreloader;
use ClassPreloader\Parser\AbstractNodeVisitor;
/**
* This is the config class.
*
* This contains all the class preloader configuration.
*/
class Config implements \IteratorAggregate
{
/**
* The array of AbstractNodeVisitor objects that visit nodes.
*
* @var array
*/
protected $visitors = array();
/**
* The array of file names.
*
* @var array
*/
protected $filenames = array();
/**
* The array of exclusive filters.
*
* @var array
*/
protected $exclusiveFilters = array();
/**
* The array of inclusive filters.
*
* @var array
*/
protected $inclusiveFilters = array();
/**
* Add the filename owned by the config.
*
* @param string $filename
*
* @return \ClassPreloader\Config
*/
public function addFile($filename)
{
$this->filenames[] = $filename;
return $this;
}
/**
* Get an array of file names that satisfy any added filters.
*
* @return array
*/
public function getFilenames()
{
$filenames = array();
foreach ($this->filenames as $f) {
foreach ($this->inclusiveFilters as $filter) {
if (!preg_match($filter, $f)) {
continue 2;
}
}
foreach ($this->exclusiveFilters as $filter) {
if (preg_match($filter, $f)) {
continue 2;
}
}
$filenames[] = $f;
}
return $filenames;
}
/**
* Get an iterator for the filenames.
*
* @return \ArrayIterator
*/
public function getIterator()
{
return new \ArrayIterator($this->getFilenames());
}
/**
* Add a filter used to filter out file names matching the pattern.
*
* We're filtering the classes using a regular expression.
*
* @param string $pattern
*
* @return \ClassPreloader\Config
*/
public function addExclusiveFilter($pattern)
{
$this->exclusiveFilters[] = $pattern;
return $this;
}
/**
* Add a filter used to grab only file names matching the pattern.
*
* We're filtering the classes using a regular expression.
*
* @param string $pattern Regular expression pattern
*
* @return \ClassPreloader\Config
*/
public function addInclusiveFilter($pattern)
{
$this->inclusiveFilters[] = $pattern;
return $this;
}
/**
* Add a visitor.
*
* It will visit each node when traversing the node list of each file.
*
* @param \ClassPreloader\Parser\AbstractNodeVisitor $visitor
*
* @return \ClassPreloader\Config
*/
public function addVisitor(AbstractNodeVisitor $visitor)
{
$this->visitors[] = $visitor;
return $this;
}
/**
* Get an array of node visitors.
*
* @return array
*/
public function getVisitors()
{
return $this->visitors;
}
}

View File

@@ -0,0 +1,13 @@
<?php
namespace ClassPreloader\Exception;
use Exception;
/**
* This is the skip file exception class.
*/
class SkipFileException extends Exception
{
//
}

View File

@@ -0,0 +1,54 @@
<?php
namespace ClassPreloader\Parser;
use PhpParser\NodeVisitorAbstract;
/**
* This is the abstract node visitor class.
*
* This is used to track the filename.
*/
abstract class AbstractNodeVisitor extends NodeVisitorAbstract
{
/**
* The current file being parsed.
*
* @var string
*/
protected $filename = '';
/**
* Set the full path to the current file being parsed.
*
* @param string $filename
*
* @return \ClassPreloader\Parser\AbstractNodeVisitor
*/
public function setFilename($filename)
{
$this->filename = $filename;
return $this;
}
/**
* Get the full path to the current file being parsed.
*
* @return string
*/
public function getFilename()
{
return $this->filename;
}
/**
* Get the directory of the current file being parsed.
*
* @return string
*/
public function getDir()
{
return dirname($this->getFilename());
}
}

View File

@@ -0,0 +1,53 @@
<?php
namespace ClassPreloader\Parser;
use ClassPreloader\Exception\SkipFileException;
use PhpParser\Node;
use PhpParser\Node\Scalar\MagicConst\Dir as DirNode;
use PhpParser\Node\Scalar\String_ as StringNode;
/**
* This is the directory node visitor class.
*
* This is used to replace all references to __DIR__ with the actual directory.
*/
class DirVisitor extends AbstractNodeVisitor
{
/**
* Should we skip the file if it contains a dir constant?
*
* @var bool
*/
protected $skip = false;
/**
* Create a new directory visitor.
*
* @param bool $skip
*
* @return void
*/
public function __construct($skip = false)
{
$this->skip = $skip;
}
/**
* Enter and modify the node.
*
* @param \PhpParser\Node $node
*
* @return void
*/
public function enterNode(Node $node)
{
if ($node instanceof DirNode) {
if ($this->skip) {
throw new SkipFileException('__DIR__ constant found, skipping...');
}
return new StringNode($this->getDir());
}
}
}

View File

@@ -0,0 +1,53 @@
<?php
namespace ClassPreloader\Parser;
use ClassPreloader\Exception\SkipFileException;
use PhpParser\Node;
use PhpParser\Node\Scalar\MagicConst\File as FileNode;
use PhpParser\Node\Scalar\String_ as StringNode;
/**
* This is the file node visitor class.
*
* This is used to replace all references to __FILE__ with the actual file.
*/
class FileVisitor extends AbstractNodeVisitor
{
/**
* Should we skip the file if it contains a file constant?
*
* @var bool
*/
protected $skip = false;
/**
* Create a new file visitor.
*
* @param bool $skip
*
* @return void
*/
public function __construct($skip = false)
{
$this->skip = $skip;
}
/**
* Enter and modify the node.
*
* @param \PhpParser\Node $node
*
* @return void
*/
public function enterNode(Node $node)
{
if ($node instanceof FileNode) {
if ($this->skip) {
throw new SkipFileException('__FILE__ constant found, skipping...');
}
return new StringNode($this->getFilename());
}
}
}

View File

@@ -0,0 +1,33 @@
<?php
namespace ClassPreloader\Parser;
use PhpParser\NodeTraverser as BaseTraverser;
/**
* This is the file node visitor class.
*
* This allows a filename to be set when visiting.
*/
class NodeTraverser extends BaseTraverser
{
/**
* Transverse the file.
*
* @param array $nodes
* @param string $filename
*
* @return void
*/
public function traverseFile(array $nodes, $filename)
{
// Set the correct state on each visitor
foreach ($this->visitors as $visitor) {
if ($visitor instanceof AbstractNodeVisitor) {
$visitor->setFilename($filename);
}
}
return $this->traverse($nodes);
}
}