Add Composer autoloading functions and PHPStan for testing

This commit is contained in:
Alex Cabal 2019-02-26 13:03:45 -06:00
parent e198c4db65
commit f5d7d4e02a
1518 changed files with 169063 additions and 30 deletions

39
vendor/nette/robot-loader/composer.json vendored Normal file
View file

@ -0,0 +1,39 @@
{
"name": "nette/robot-loader",
"description": "🍀 Nette RobotLoader: high performance and comfortable autoloader that will search and autoload classes within your application.",
"keywords": ["nette", "autoload", "class", "trait", "interface"],
"homepage": "https://nette.org",
"license": ["BSD-3-Clause", "GPL-2.0", "GPL-3.0"],
"authors": [
{
"name": "David Grudl",
"homepage": "https://davidgrudl.com"
},
{
"name": "Nette Community",
"homepage": "https://nette.org/contributors"
}
],
"require": {
"php": ">=5.6.0",
"ext-tokenizer": "*",
"nette/finder": "^2.3 || ^3.0",
"nette/utils": "^2.4 || ^3.0"
},
"require-dev": {
"nette/tester": "^2.0",
"tracy/tracy": "^2.3"
},
"conflict": {
"nette/nette": "<2.2"
},
"autoload": {
"classmap": ["src/"]
},
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-master": "3.1-dev"
}
}
}

View file

@ -0,0 +1,33 @@
How to contribute & use the issue tracker
=========================================
Nette welcomes your contributions. There are several ways to help out:
* Create an issue on GitHub, if you have found a bug
* Write test cases for open bug issues
* Write fixes for open bug/feature issues, preferably with test cases included
* Contribute to the [documentation](https://nette.org/en/writing)
Issues
------
Please **do not use the issue tracker to ask questions**. We will be happy to help you
on [Nette forum](https://forum.nette.org) or chat with us on [Gitter](https://gitter.im/nette/nette).
A good bug report shouldn't leave others needing to chase you up for more
information. Please try to be as detailed as possible in your report.
**Feature requests** are welcome. But take a moment to find out whether your idea
fits with the scope and aims of the project. It's up to *you* to make a strong
case to convince the project's developers of the merits of this feature.
Contributing
------------
If you'd like to contribute, please take a moment to read [the contributing guide](https://nette.org/en/contributing).
The best way to propose a feature is to discuss your ideas on [Nette forum](https://forum.nette.org) before implementing them.
Please do not fix whitespace, format code, or make a purely cosmetic patch.
Thanks! :heart:

60
vendor/nette/robot-loader/license.md vendored Normal file
View file

@ -0,0 +1,60 @@
Licenses
========
Good news! You may use Nette Framework under the terms of either
the New BSD License or the GNU General Public License (GPL) version 2 or 3.
The BSD License is recommended for most projects. It is easy to understand and it
places almost no restrictions on what you can do with the framework. If the GPL
fits better to your project, you can use the framework under this license.
You don't have to notify anyone which license you are using. You can freely
use Nette Framework in commercial projects as long as the copyright header
remains intact.
Please be advised that the name "Nette Framework" is a protected trademark and its
usage has some limitations. So please do not use word "Nette" in the name of your
project or top-level domain, and choose a name that stands on its own merits.
If your stuff is good, it will not take long to establish a reputation for yourselves.
New BSD License
---------------
Copyright (c) 2004, 2014 David Grudl (https://davidgrudl.com)
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of "Nette Framework" nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
This software is provided by the copyright holders and contributors "as is" and
any express or implied warranties, including, but not limited to, the implied
warranties of merchantability and fitness for a particular purpose are
disclaimed. In no event shall the copyright owner or contributors be liable for
any direct, indirect, incidental, special, exemplary, or consequential damages
(including, but not limited to, procurement of substitute goods or services;
loss of use, data, or profits; or business interruption) however caused and on
any theory of liability, whether in contract, strict liability, or tort
(including negligence or otherwise) arising in any way out of the use of this
software, even if advised of the possibility of such damage.
GNU General Public License
--------------------------
GPL licenses are very very long, so instead of including them here we offer
you URLs with full text:
- [GPL version 2](http://www.gnu.org/licenses/gpl-2.0.html)
- [GPL version 3](http://www.gnu.org/licenses/gpl-3.0.html)

78
vendor/nette/robot-loader/readme.md vendored Normal file
View file

@ -0,0 +1,78 @@
RobotLoader: comfortable autoloading
====================================
[![Downloads this Month](https://img.shields.io/packagist/dm/nette/robot-loader.svg)](https://packagist.org/packages/nette/robot-loader)
[![Build Status](https://travis-ci.org/nette/robot-loader.svg?branch=master)](https://travis-ci.org/nette/robot-loader)
[![Coverage Status](https://coveralls.io/repos/github/nette/robot-loader/badge.svg?branch=master)](https://coveralls.io/github/nette/robot-loader?branch=master)
[![Latest Stable Version](https://poser.pugx.org/nette/robot-loader/v/stable)](https://github.com/nette/robot-loader/releases)
[![License](https://img.shields.io/badge/license-New%20BSD-blue.svg)](https://github.com/nette/robot-loader/blob/master/license.md)
Introduction
------------
RobotLoader is a tool that gives you comfort of automated class loading for your entire application including third-party libraries.
- get rid of all `require`
- only necessary scripts are loaded
- requires no strict file naming conventions
- allows more classes in single file
RobotLoader is extremely comfortable and addictive!
If you like Nette, **[please make a donation now](https://nette.org/donate)**. Thank you!
So we can forget about those famous code blocks:
```php
require_once 'Utils/Page.php';
require_once 'Utils/Style.php';
require_once 'Utils/Paginator.php';
...
```
Like the Google robot crawls and indexes websites, RobotLoader crawls all PHP scripts and records what classes and interfaces were found in them.
These records are then saved in cache and used during all subsequent requests.
Documentation can be found on the [website](https://doc.nette.org/robotloader).
Installation
------------
The recommended way to install is via Composer:
```
composer require nette/robot-loader
```
It requires PHP version 5.6 and supports PHP up to 7.2.
Usage
-----
You just need to specifiy what directories to index and where to save the cache:
```php
$loader = new Nette\Loaders\RobotLoader;
// Add directories for RobotLoader to index
$loader->addDirectory(__DIR__ . '/app');
$loader->addDirectory(__DIR__ . '/libs');
// And set caching to the 'temp' directory
$loader->setTempDirectory(__DIR__ . '/temp');
$loader->register(); // Run the RobotLoader
```
And that's all. From now on, you don't need to use `require`. Great, isn't it?
When RobotLoader encounters duplicate class name during indexing, it throws an exception and informs you about it.
The `$loader->setAutoRefresh(true or false)` determines whether RobotLoader should reindex files if asked for nonexistent class.
This feature should be disabled on production server.
If you want RobotLoader to skip some directory, use `$loader->excludeDirectory('temp')`.
By default, RobotLoader reports errors in PHP files by throwing exception `ParseError` (since PHP 7.0). It can be disabled via `$loader->reportParseErrors(false)`.

View file

@ -0,0 +1,471 @@
<?php
/**
* This file is part of the Nette Framework (https://nette.org)
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
*/
namespace Nette\Loaders;
use Nette;
use SplFileInfo;
/**
* Nette auto loader is responsible for loading classes and interfaces.
*
* <code>
* $loader = new Nette\Loaders\RobotLoader;
* $loader->addDirectory('app');
* $loader->excludeDirectory('app/exclude');
* $loader->setTempDirectory('temp');
* $loader->register();
* </code>
*/
class RobotLoader
{
use Nette\SmartObject;
const RETRY_LIMIT = 3;
/** @var array comma separated wildcards */
public $ignoreDirs = ['.*', '*.old', '*.bak', '*.tmp', 'temp'];
/** @var array comma separated wildcards */
public $acceptFiles = ['*.php'];
/** @var bool */
private $autoRebuild = true;
/** @var bool */
private $reportParseErrors = true;
/** @var array */
private $scanPaths = [];
/** @var array */
private $excludeDirs = [];
/** @var array of class => [file, time] */
private $classes = [];
/** @var bool */
private $refreshed = false;
/** @var array of missing classes */
private $missing = [];
/** @var string|null */
private $tempDirectory;
public function __construct()
{
if (!extension_loaded('tokenizer')) {
throw new Nette\NotSupportedException('PHP extension Tokenizer is not loaded.');
}
}
/**
* Register autoloader.
* @param bool $prepend
* @return static
*/
public function register($prepend = false)
{
$this->loadCache();
spl_autoload_register([$this, 'tryLoad'], true, $prepend);
return $this;
}
/**
* Handles autoloading of classes, interfaces or traits.
* @param string $type
* @return void
*/
public function tryLoad($type)
{
$type = ltrim($type, '\\'); // PHP namespace bug #49143
$info = isset($this->classes[$type]) ? $this->classes[$type] : null;
if ($this->autoRebuild) {
if (!$info || !is_file($info['file'])) {
$missing = &$this->missing[$type];
$missing++;
if (!$this->refreshed && $missing <= self::RETRY_LIMIT) {
$this->refresh();
$this->saveCache();
} elseif ($info) {
unset($this->classes[$type]);
$this->saveCache();
}
} elseif (!$this->refreshed && filemtime($info['file']) !== $info['time']) {
$this->updateFile($info['file']);
if (empty($this->classes[$type])) {
$this->missing[$type] = 0;
}
$this->saveCache();
}
$info = isset($this->classes[$type]) ? $this->classes[$type] : null;
}
if ($info) {
call_user_func(function ($file) { require $file; }, $info['file']);
}
}
/**
* Add path or paths to list.
* @param string|string[] $path absolute path
* @return static
*/
public function addDirectory($path)
{
$this->scanPaths = array_merge($this->scanPaths, (array) $path);
return $this;
}
/**
* @return static
*/
public function reportParseErrors($on = true)
{
$this->reportParseErrors = (bool) $on;
return $this;
}
/**
* Excludes path or paths from list.
* @param string|string[] $path absolute path
* @return static
*/
public function excludeDirectory($path)
{
$this->excludeDirs = array_merge($this->excludeDirs, (array) $path);
return $this;
}
/**
* @return array of class => filename
*/
public function getIndexedClasses()
{
$res = [];
foreach ($this->classes as $class => $info) {
$res[$class] = $info['file'];
}
return $res;
}
/**
* Rebuilds class list cache.
* @return void
*/
public function rebuild()
{
$this->refresh();
if ($this->tempDirectory) {
$this->saveCache();
}
}
/**
* Refreshes class list.
* @return void
*/
private function refresh()
{
$this->refreshed = true; // prevents calling refresh() or updateFile() in tryLoad()
$files = [];
foreach ($this->classes as $class => $info) {
$files[$info['file']]['time'] = $info['time'];
$files[$info['file']]['classes'][] = $class;
}
$this->classes = [];
foreach ($this->scanPaths as $path) {
foreach (is_file($path) ? [new SplFileInfo($path)] : $this->createFileIterator($path) as $file) {
$file = $file->getPathname();
if (isset($files[$file]) && $files[$file]['time'] == filemtime($file)) {
$classes = $files[$file]['classes'];
} else {
$classes = $this->scanPhp($file);
}
$files[$file] = ['classes' => [], 'time' => filemtime($file)];
foreach ($classes as $class) {
$info = &$this->classes[$class];
if (isset($info['file'])) {
throw new Nette\InvalidStateException("Ambiguous class $class resolution; defined in {$info['file']} and in $file.");
}
$info = ['file' => $file, 'time' => filemtime($file)];
unset($this->missing[$class]);
}
}
}
}
/**
* Creates an iterator scaning directory for PHP files, subdirectories and 'netterobots.txt' files.
* @return Nette\Utils\Finder
* @throws Nette\IOException if path is not found
*/
private function createFileIterator($dir)
{
if (!is_dir($dir)) {
throw new Nette\IOException("File or directory '$dir' not found.");
}
$ignoreDirs = is_array($this->ignoreDirs) ? $this->ignoreDirs : preg_split('#[,\s]+#', $this->ignoreDirs);
$disallow = [];
foreach (array_merge($ignoreDirs, $this->excludeDirs) as $item) {
if ($item = realpath($item)) {
$disallow[str_replace('\\', '/', $item)] = true;
}
}
$iterator = Nette\Utils\Finder::findFiles(is_array($this->acceptFiles) ? $this->acceptFiles : preg_split('#[,\s]+#', $this->acceptFiles))
->filter(function (SplFileInfo $file) use (&$disallow) {
return !isset($disallow[str_replace('\\', '/', $file->getRealPath())]);
})
->from($dir)
->exclude($ignoreDirs)
->filter($filter = function (SplFileInfo $dir) use (&$disallow) {
$path = str_replace('\\', '/', $dir->getRealPath());
if (is_file("$path/netterobots.txt")) {
foreach (file("$path/netterobots.txt") as $s) {
if (preg_match('#^(?:disallow\\s*:)?\\s*(\\S+)#i', $s, $matches)) {
$disallow[$path . rtrim('/' . ltrim($matches[1], '/'), '/')] = true;
}
}
}
return !isset($disallow[$path]);
});
$filter(new SplFileInfo($dir));
return $iterator;
}
/**
* @return void
*/
private function updateFile($file)
{
foreach ($this->classes as $class => $info) {
if (isset($info['file']) && $info['file'] === $file) {
unset($this->classes[$class]);
}
}
$classes = is_file($file) ? $this->scanPhp($file) : [];
foreach ($classes as $class) {
$info = &$this->classes[$class];
if (isset($info['file']) && @filemtime($info['file']) !== $info['time']) { // @ file may not exists
$this->updateFile($info['file']);
$info = &$this->classes[$class];
}
if (isset($info['file'])) {
throw new Nette\InvalidStateException("Ambiguous class $class resolution; defined in {$info['file']} and in $file.");
}
$info = ['file' => $file, 'time' => filemtime($file)];
}
}
/**
* Searches classes, interfaces and traits in PHP file.
* @param string $file
* @return string[]
*/
private function scanPhp($file)
{
$code = file_get_contents($file);
$expected = false;
$namespace = '';
$level = $minLevel = 0;
$classes = [];
if (preg_match('#//nette' . 'loader=(\S*)#', $code, $matches)) {
foreach (explode(',', $matches[1]) as $name) {
$classes[] = $name;
}
return $classes;
}
try {
$tokens = PHP_VERSION_ID >= 70000
? token_get_all($code, TOKEN_PARSE)
: @token_get_all($code); // @ can be corrupted or can use newer syntax
} catch (\ParseError $e) {
if ($this->reportParseErrors) {
$rp = new \ReflectionProperty($e, 'file');
$rp->setAccessible(true);
$rp->setValue($e, $file);
throw $e;
}
$tokens = [];
}
foreach ($tokens as $token) {
if (is_array($token)) {
switch ($token[0]) {
case T_COMMENT:
case T_DOC_COMMENT:
case T_WHITESPACE:
continue 2;
case T_NS_SEPARATOR:
case T_STRING:
if ($expected) {
$name .= $token[1];
}
continue 2;
case T_NAMESPACE:
case T_CLASS:
case T_INTERFACE:
case T_TRAIT:
$expected = $token[0];
$name = '';
continue 2;
case T_CURLY_OPEN:
case T_DOLLAR_OPEN_CURLY_BRACES:
$level++;
}
}
if ($expected) {
switch ($expected) {
case T_CLASS:
case T_INTERFACE:
case T_TRAIT:
if ($name && $level === $minLevel) {
$classes[] = $namespace . $name;
}
break;
case T_NAMESPACE:
$namespace = $name ? $name . '\\' : '';
$minLevel = $token === '{' ? 1 : 0;
}
$expected = null;
}
if ($token === '{') {
$level++;
} elseif ($token === '}') {
$level--;
}
}
return $classes;
}
/********************* caching ****************d*g**/
/**
* Sets auto-refresh mode.
* @return static
*/
public function setAutoRefresh($on = true)
{
$this->autoRebuild = (bool) $on;
return $this;
}
/**
* Sets path to temporary directory.
* @return static
*/
public function setTempDirectory($dir)
{
Nette\Utils\FileSystem::createDir($dir);
$this->tempDirectory = $dir;
return $this;
}
/**
* Loads class list from cache.
* @return void
*/
private function loadCache()
{
$file = $this->getCacheFile();
list($this->classes, $this->missing) = @include $file; // @ file may not exist
if (is_array($this->classes)) {
return;
}
$handle = fopen("$file.lock", 'c+');
if (!$handle || !flock($handle, LOCK_EX)) {
throw new \RuntimeException("Unable to create or acquire exclusive lock on file '$file.lock'.");
}
list($this->classes, $this->missing) = @include $file; // @ file may not exist
if (!is_array($this->classes)) {
$this->classes = [];
$this->refresh();
$this->saveCache();
}
flock($handle, LOCK_UN);
fclose($handle);
@unlink("$file.lock"); // @ file may become locked on Windows
}
/**
* Writes class list to cache.
* @return void
*/
private function saveCache()
{
$file = $this->getCacheFile();
$tempFile = $file . uniqid('', true) . '.tmp';
$code = "<?php\nreturn " . var_export([$this->classes, $this->missing], true) . ";\n";
if (file_put_contents($tempFile, $code) !== strlen($code) || !rename($tempFile, $file)) {
@unlink($tempFile); // @ - file may not exist
throw new \RuntimeException("Unable to create '$file'.");
}
if (function_exists('opcache_invalidate')) {
@opcache_invalidate($file, true); // @ can be restricted
}
}
/**
* @return string
*/
private function getCacheFile()
{
if (!$this->tempDirectory) {
throw new \LogicException('Set path to temporary directory using setTempDirectory().');
}
return $this->tempDirectory . '/' . md5(serialize($this->getCacheKey())) . '.php';
}
/**
* @return array
*/
protected function getCacheKey()
{
return [$this->ignoreDirs, $this->acceptFiles, $this->scanPaths, $this->excludeDirs];
}
}