no message

This commit is contained in:
BarboraFleg
2019-04-04 18:34:40 +02:00
commit ce48ca4a21
589 changed files with 82184 additions and 0 deletions
+39
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"
}
}
}
+33
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
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)
+113
View File
@@ -0,0 +1,113 @@
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`
- requires no strict file naming conventions
- allows more classes in single file
- extremely fast
- no manual cache updates, everything runs automatically
- highly mature, stable and widely used library
RobotLoader is incredibly 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.3.
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)`.
PHP files analyzer
------------------
RobotLoader can also be used to find classes, interfaces, and trait in PHP files without using the autoloading feature:
```php
$loader = new Nette\Loaders\RobotLoader;
$loader->addDirectory(__DIR__ . '/app');
// Scans directories for classes / intefaces / traits
$loader->rebuild();
// Returns array of class => filename pairs
$res = $loader->getIndexedClasses();
```
When scanning files again, we can use the cache and unmodified files will not be analyzed repeatedly:
```php
$loader = new Nette\Loaders\RobotLoader;
$loader->addDirectory(__DIR__ . '/app');
$loader->setTempDirectory(__DIR__ . '/temp');
// Scans directories using a cache
$loader->refresh();
// Returns array of class => filename pairs
$res = $loader->getIndexedClasses();
```
Enjoy RobotLoader!
@@ -0,0 +1,486 @@
<?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 */
public $ignoreDirs = ['.*', '*.old', '*.bak', '*.tmp', 'temp'];
/** @var array */
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->refreshClasses();
$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->classes = $this->missing = [];
$this->refreshClasses();
if ($this->tempDirectory) {
$this->saveCache();
}
}
/**
* Refreshes class list cache.
* @return void
*/
public function refresh()
{
$this->loadCache();
if (!$this->refreshed) {
$this->refreshClasses();
$this->saveCache();
}
}
/**
* Refreshes $classes.
* @return void
*/
private function refreshClasses()
{
$this->refreshed = true; // prevents calling refreshClasses() 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) {
$iterator = is_file($path) ? [new SplFileInfo($path)] : $this->createFileIterator($path);
foreach ($iterator 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;
}
}
$acceptFiles = is_array($this->acceptFiles) ? $this->acceptFiles : preg_split('#[,\s]+#', $this->acceptFiles);
$iterator = Nette\Utils\Finder::findFiles($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->rebuild();
}
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];
}
}