├── .gitignore
├── vendor
├── composer
│ ├── installed.json
│ ├── autoload_namespaces.php
│ ├── autoload_psr4.php
│ ├── autoload_files.php
│ ├── autoload_classmap.php
│ ├── installed.php
│ ├── LICENSE
│ ├── autoload_static.php
│ ├── autoload_real.php
│ ├── InstalledVersions.php
│ └── ClassLoader.php
└── autoload.php
├── core
├── socket
│ ├── SocketServer.php
│ ├── Sockets.php
│ └── SocketListen.php
├── coroutines
│ ├── CoroutineReturnValue.php
│ ├── SystemCall.php
│ ├── CoSocket.php
│ ├── Task.php
│ └── Scheduler.php
└── common.php
├── README.md
├── composer.json
├── curl.php
├── composer.lock
├── start.php
├── test.php
└── CoStart.php
/.gitignore:
--------------------------------------------------------------------------------
1 | *.DS_Store
2 | !.gitignore
3 | .idea*
4 | *runtime
5 | composer.lock
--------------------------------------------------------------------------------
/vendor/composer/installed.json:
--------------------------------------------------------------------------------
1 | {
2 | "packages": [],
3 | "dev": true,
4 | "dev-package-names": []
5 | }
6 |
--------------------------------------------------------------------------------
/core/socket/SocketServer.php:
--------------------------------------------------------------------------------
1 | php 协程 http封装
4 |
5 | https://www.laruence.com/2015/05/28/3038.html
6 |
--------------------------------------------------------------------------------
/vendor/autoload.php:
--------------------------------------------------------------------------------
1 | array($baseDir . '/core'),
10 | );
11 |
--------------------------------------------------------------------------------
/vendor/composer/autoload_files.php:
--------------------------------------------------------------------------------
1 | $baseDir . '/core/common.php',
10 | );
11 |
--------------------------------------------------------------------------------
/vendor/composer/autoload_classmap.php:
--------------------------------------------------------------------------------
1 | $vendorDir . '/composer/InstalledVersions.php',
10 | );
11 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "php/php-yield-webserver",
3 | "authors": [
4 | {
5 | "name": "nekgod",
6 | "email": "1559096467@qq.com"
7 | }
8 | ],
9 | "autoload": {
10 | "psr-4": {
11 | "core\\": "core/"
12 | },
13 | "files": ["core/common.php"]
14 | },
15 | "require": {}
16 | }
17 |
--------------------------------------------------------------------------------
/curl.php:
--------------------------------------------------------------------------------
1 | newTask(test());
15 | $scheduler->run();
--------------------------------------------------------------------------------
/core/coroutines/CoroutineReturnValue.php:
--------------------------------------------------------------------------------
1 | value = $value;
14 | }
15 |
16 | public function getValue()
17 | {
18 | return $this->value;
19 | }
20 | }
--------------------------------------------------------------------------------
/core/coroutines/SystemCall.php:
--------------------------------------------------------------------------------
1 | callback = $callback;
18 | }
19 |
20 | public function __invoke(Task $task, Scheduler $scheduler)
21 | {
22 | $callback = $this->callback;
23 | return $callback($task, $scheduler);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/composer.lock:
--------------------------------------------------------------------------------
1 | {
2 | "_readme": [
3 | "This file locks the dependencies of your project to a known state",
4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
5 | "This file is @generated automatically"
6 | ],
7 | "content-hash": "c2bb16a30b43905b0d6682b7ef866fee",
8 | "packages": [],
9 | "packages-dev": [],
10 | "aliases": [],
11 | "minimum-stability": "stable",
12 | "stability-flags": [],
13 | "prefer-stable": false,
14 | "prefer-lowest": false,
15 | "platform": [],
16 | "platform-dev": [],
17 | "plugin-api-version": "2.0.0"
18 | }
19 |
--------------------------------------------------------------------------------
/start.php:
--------------------------------------------------------------------------------
1 | popSocket();
15 | var_dump($socket);
16 | $content = $socket->getContent();
17 | var_dump($content);
18 | $s = $socket->sendContent("HTTP/1.1 200 OK
19 | Content-Type: text/html
20 |
21 | hello world\r");
22 | var_dump($s);
23 | $socket->close();
24 | }while(true);
25 |
26 | socket_close($socketListen->getSocket());
--------------------------------------------------------------------------------
/test.php:
--------------------------------------------------------------------------------
1 | current();
33 | //var_dump($res);
34 | //$Scheduler = new Scheduler();
35 | //$Scheduler->newTask(test1());
36 | //$Scheduler->run();
--------------------------------------------------------------------------------
/core/coroutines/CoSocket.php:
--------------------------------------------------------------------------------
1 | socket = $socket;
11 | }
12 |
13 | public function accept()
14 | {
15 | yield waitForRead($this->socket);
16 | yield retval(new CoSocket(stream_socket_accept($this->socket, 0)));
17 | }
18 |
19 | public function read($size)
20 | {
21 | yield waitForRead($this->socket);
22 | yield retval(fread($this->socket, $size));
23 | }
24 |
25 | public function write($string)
26 | {
27 | yield waitForWrite($this->socket);
28 | fwrite($this->socket, $string);
29 | }
30 |
31 | public function close()
32 | {
33 | @fclose($this->socket);
34 | }
35 | }
--------------------------------------------------------------------------------
/core/socket/Sockets.php:
--------------------------------------------------------------------------------
1 | socket = $socket;
12 | }
13 |
14 | public function getContent()
15 | {
16 | if ($this->readContent) {
17 | return $this->readContent;
18 | }
19 | $this->readContent = socket_read($this->socket, 1024 * 1024);
20 | return $this->readContent;
21 | }
22 |
23 | public function sendContent($content)
24 | {
25 | return socket_write($this->socket, $content,strlen($content));
26 | }
27 |
28 | public function close()
29 | {
30 | socket_close($this->socket);
31 | }
32 |
33 | public function getSocket()
34 | {
35 | return $this->socket;
36 | }
37 | }
--------------------------------------------------------------------------------
/vendor/composer/installed.php:
--------------------------------------------------------------------------------
1 | array(
3 | 'pretty_version' => 'dev-master',
4 | 'version' => 'dev-master',
5 | 'type' => 'library',
6 | 'install_path' => __DIR__ . '/../../',
7 | 'aliases' => array(),
8 | 'reference' => '543cc1481efb0acbdfd514a350c27cd45da43129',
9 | 'name' => 'php/php-yield-webserver',
10 | 'dev' => true,
11 | ),
12 | 'versions' => array(
13 | 'php/php-yield-webserver' => array(
14 | 'pretty_version' => 'dev-master',
15 | 'version' => 'dev-master',
16 | 'type' => 'library',
17 | 'install_path' => __DIR__ . '/../../',
18 | 'aliases' => array(),
19 | 'reference' => '543cc1481efb0acbdfd514a350c27cd45da43129',
20 | 'dev_requirement' => false,
21 | ),
22 | ),
23 | );
24 |
--------------------------------------------------------------------------------
/core/socket/SocketListen.php:
--------------------------------------------------------------------------------
1 | socket = $sock;
20 | }
21 |
22 | /**
23 | * @return Sockets
24 | */
25 | public function popSocket()
26 | {
27 | return new Sockets(socket_accept($this->getSocket()));
28 | }
29 |
30 | public function getSocket(){
31 | return $this->socket;
32 | }
33 |
34 | }
--------------------------------------------------------------------------------
/vendor/composer/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Copyright (c) Nils Adermann, Jordi Boggiano
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy
5 | of this software and associated documentation files (the "Software"), to deal
6 | in the Software without restriction, including without limitation the rights
7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | copies of the Software, and to permit persons to whom the Software is furnished
9 | to do so, subject to the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be included in all
12 | copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | THE SOFTWARE.
21 |
22 |
--------------------------------------------------------------------------------
/core/coroutines/Task.php:
--------------------------------------------------------------------------------
1 | taskId = $taskId;
20 | $this->coroutine = StackedCoroutine($coroutine);
21 | }
22 |
23 | public function getTaskId()
24 | {
25 | return $this->taskId;
26 | }
27 |
28 | public function setSendValue($sendValue)
29 | {
30 | $this->sendValue = $sendValue;
31 | }
32 |
33 | public function run()
34 | {
35 | if ($this->beforeFirstYield) {
36 | $this->beforeFirstYield = false;
37 | return $this->coroutine->current();
38 | }
39 |
40 | $retrieval = $this->coroutine->send($this->sendValue);
41 | $this->sendValue = null;
42 | return $retrieval;
43 | }
44 |
45 | /**
46 | * 任务是否已结束
47 | * @return bool
48 | */
49 | public function isFinished(): bool
50 | {
51 | return !$this->coroutine->valid();
52 | }
53 | }
--------------------------------------------------------------------------------
/CoStart.php:
--------------------------------------------------------------------------------
1 | accept())
18 | );
19 | }
20 | }
21 |
22 | /**
23 | * @param CoSocket $socket
24 | * @return \Generator
25 | */
26 | function handleClient(CoSocket $socket): Generator
27 | {
28 | $data = (yield $socket->read(8192));
29 | $msg = "Received following request:\n\n";
30 | $msgLength = strlen($msg);
31 | $response = <<write($response);
39 | yield $socket->close();
40 | }
41 | $scheduler = new Scheduler;
42 | try {
43 | $scheduler->newTask(server(8000));
44 | /** @noinspection PhpUnreachableStatementInspection */
45 | $scheduler->run();
46 | } catch (Exception $e) {
47 |
48 | }
--------------------------------------------------------------------------------
/vendor/composer/autoload_static.php:
--------------------------------------------------------------------------------
1 | __DIR__ . '/../..' . '/core/common.php',
11 | );
12 |
13 | public static $prefixLengthsPsr4 = array (
14 | 'c' =>
15 | array (
16 | 'core\\' => 5,
17 | ),
18 | );
19 |
20 | public static $prefixDirsPsr4 = array (
21 | 'core\\' =>
22 | array (
23 | 0 => __DIR__ . '/../..' . '/core',
24 | ),
25 | );
26 |
27 | public static $classMap = array (
28 | 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
29 | );
30 |
31 | public static function getInitializer(ClassLoader $loader)
32 | {
33 | return \Closure::bind(function () use ($loader) {
34 | $loader->prefixLengthsPsr4 = ComposerStaticInitacba2ce365c74acc98a9fb19fde0dc62::$prefixLengthsPsr4;
35 | $loader->prefixDirsPsr4 = ComposerStaticInitacba2ce365c74acc98a9fb19fde0dc62::$prefixDirsPsr4;
36 | $loader->classMap = ComposerStaticInitacba2ce365c74acc98a9fb19fde0dc62::$classMap;
37 |
38 | }, null, ClassLoader::class);
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/vendor/composer/autoload_real.php:
--------------------------------------------------------------------------------
1 | = 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
30 | if ($useStaticLoader) {
31 | require __DIR__ . '/autoload_static.php';
32 |
33 | call_user_func(\Composer\Autoload\ComposerStaticInitacba2ce365c74acc98a9fb19fde0dc62::getInitializer($loader));
34 | } else {
35 | $map = require __DIR__ . '/autoload_namespaces.php';
36 | foreach ($map as $namespace => $path) {
37 | $loader->set($namespace, $path);
38 | }
39 |
40 | $map = require __DIR__ . '/autoload_psr4.php';
41 | foreach ($map as $namespace => $path) {
42 | $loader->setPsr4($namespace, $path);
43 | }
44 |
45 | $classMap = require __DIR__ . '/autoload_classmap.php';
46 | if ($classMap) {
47 | $loader->addClassMap($classMap);
48 | }
49 | }
50 |
51 | $loader->register(true);
52 |
53 | if ($useStaticLoader) {
54 | $includeFiles = Composer\Autoload\ComposerStaticInitacba2ce365c74acc98a9fb19fde0dc62::$files;
55 | } else {
56 | $includeFiles = require __DIR__ . '/autoload_files.php';
57 | }
58 | foreach ($includeFiles as $fileIdentifier => $file) {
59 | composerRequireacba2ce365c74acc98a9fb19fde0dc62($fileIdentifier, $file);
60 | }
61 |
62 | return $loader;
63 | }
64 | }
65 |
66 | /**
67 | * @param string $fileIdentifier
68 | * @param string $file
69 | * @return void
70 | */
71 | function composerRequireacba2ce365c74acc98a9fb19fde0dc62($fileIdentifier, $file)
72 | {
73 | if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
74 | $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
75 |
76 | require $file;
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/core/common.php:
--------------------------------------------------------------------------------
1 | setSendValue($task->getTaskId());
16 | $scheduler->schedule($task);
17 | });
18 | }
19 |
20 | /**
21 | * 创建新任务
22 | * @param Generator $coroutine
23 | * @return SystemCall
24 | */
25 | function newTask(Generator $coroutine): SystemCall
26 | {
27 | return new SystemCall(
28 | function (Task $task, Scheduler $scheduler) use ($coroutine) {
29 | $task->setSendValue($scheduler->newTask($coroutine));
30 | $scheduler->schedule($task);
31 | }
32 | );
33 | }
34 |
35 | /**
36 | * 删除任务
37 | * @param $tid
38 | * @return SystemCall
39 | */
40 | function killTask($tid): SystemCall
41 | {
42 | return new SystemCall(
43 | function (Task $task, Scheduler $scheduler) use ($tid) {
44 | $task->setSendValue($scheduler->killTask($tid));
45 | $scheduler->schedule($task);
46 | }
47 | );
48 | }
49 |
50 | /**
51 | * socket 读取
52 | * @param $socket
53 | * @return SystemCall
54 | */
55 | function waitForRead($socket): SystemCall
56 | {
57 | return new SystemCall(
58 | function (Task $task, Scheduler $scheduler) use ($socket) {
59 | $scheduler->waitForRead($socket, $task);
60 | }
61 | );
62 | }
63 |
64 | /**
65 | * socket 写入
66 | * @param $socket
67 | * @return \core\coroutines\SystemCall
68 | */
69 | function waitForWrite($socket): SystemCall
70 | {
71 | return new SystemCall(
72 | function (Task $task, Scheduler $scheduler) use ($socket) {
73 | $scheduler->waitForWrite($socket, $task);
74 | }
75 | );
76 | }
77 |
78 | /**
79 | * 协程堆栈
80 | * @param Generator $gen
81 | * @return Generator|void
82 | */
83 | function stackedCoroutine(Generator $gen)
84 | {
85 | $stack = new SplStack;
86 | for (; ;) {
87 | $value = $gen->current();
88 | if ($value instanceof Generator) {
89 | $stack->push($gen);
90 | $gen = $value;
91 | continue;
92 | }
93 | $isReturnValue = $value instanceof CoroutineReturnValue;
94 | if (!$gen->valid() || $isReturnValue) {
95 | if ($stack->isEmpty()) {
96 | return;
97 | }
98 | $gen = $stack->pop();
99 | $gen->send($isReturnValue ? $value->getValue() : NULL);
100 | continue;
101 | }
102 | $gen->send(yield $gen->key() => $value);
103 | }
104 | }
105 |
106 | /**
107 | * 创建协程返回函数
108 | * @param $value
109 | * @return CoroutineReturnValue
110 | */
111 | function retval($value): CoroutineReturnValue
112 | {
113 | return new CoroutineReturnValue($value);
114 | }
--------------------------------------------------------------------------------
/core/coroutines/Scheduler.php:
--------------------------------------------------------------------------------
1 | task
16 | protected $taskQueue; // 任务队列
17 |
18 | public function __construct()
19 | {
20 | $this->taskQueue = new SplQueue();
21 | }
22 |
23 | /**
24 | * 运行调度器 执行任务
25 | * @return void
26 | */
27 | public function run()
28 | {
29 | $this->newTask($this->ioPollTask());
30 | while (!$this->taskQueue->isEmpty()) {
31 | /** @var Task $task */
32 | $task = $this->taskQueue->dequeue();
33 | $retrieval = $task->run();
34 | /**
35 | * 任务中有系统调用函数执行
36 | */
37 | if ($retrieval instanceof SystemCall) {
38 | $retrieval($task, $this);
39 | continue;
40 | }
41 | if ($task->isFinished()) {
42 | unset($this->taskMap[$task->getTaskId()]);
43 | } else {
44 | $this->schedule($task);
45 | }
46 | }
47 | }
48 |
49 | /**
50 | * 任务 追加
51 | * @param Generator $coroutine
52 | * @return int
53 | */
54 | public function newTask(Generator $coroutine): int
55 | {
56 | $tid = ++$this->maxTaskId;
57 | $task = new Task($tid, $coroutine);
58 | $this->taskMap[$tid] = $task;
59 | $this->schedule($task);
60 | return $tid;
61 | }
62 |
63 | /**
64 | * 协程最小调度单位 yield 加入调用任务队列
65 | * @param Task $task
66 | * @return void
67 | */
68 | public function schedule(Task $task)
69 | {
70 | $this->taskQueue->enqueue($task);
71 | }
72 |
73 | /**
74 | * 删除任务
75 | * @param $tid
76 | * @return bool
77 | */
78 | public function killTask($tid): bool
79 | {
80 | if (!isset($this->taskMap[$tid])) {
81 | return false;
82 | }
83 | // 移除task任务映射
84 | unset($this->taskMap[$tid]);
85 | // 移除任务队列
86 | foreach ($this->taskQueue as $i => $task) {
87 | if ($task->getTaskId() === $tid) {
88 | unset($this->taskQueue[$i]);
89 | break;
90 | }
91 | }
92 | return true;
93 | }
94 |
95 | protected $waitingForRead = [];
96 | protected $waitingForWrite = [];
97 |
98 | // resourceID => [socket, tasks]
99 |
100 | public function waitForRead($socket, Task $task)
101 | {
102 | if (isset($this->waitingForRead[(int)$socket])) {
103 | $this->waitingForRead[(int)$socket][1][] = $task;
104 | } else {
105 | $this->waitingForRead[(int)$socket] = [$socket, [$task]];
106 | }
107 | }
108 |
109 | public function waitForWrite($socket, Task $task)
110 | {
111 | if (isset($this->waitingForWrite[(int)$socket])) {
112 | $this->waitingForWrite[(int)$socket][1][] = $task;
113 | } else {
114 | $this->waitingForWrite[(int)$socket] = [$socket, [$task]];
115 | }
116 | }
117 |
118 | protected function ioPollTask(): Generator
119 | {
120 | while (true) {
121 | if ($this->taskQueue->isEmpty()) {
122 | $this->ioPoll(null);
123 | } else {
124 | $this->ioPoll(0);
125 | }
126 | yield;
127 | }
128 | }
129 |
130 | protected function ioPoll($timeout)
131 | {
132 | $rSocks = [];
133 | foreach ($this->waitingForRead as list($socket)) {
134 | $rSocks[] = $socket;
135 | }
136 | $wSocks = [];
137 | foreach ($this->waitingForWrite as list($socket)) {
138 | $wSocks[] = $socket;
139 | }
140 | // var_dump($rSocks);
141 | // var_dump($wSocks);
142 | $eSocks = []; // dummy
143 | if (!stream_select($rSocks, $wSocks, $eSocks, $timeout)) {
144 | return;
145 | }
146 | foreach ($rSocks as $socket) {
147 | list(, $tasks) = $this->waitingForRead[(int)$socket];
148 | unset($this->waitingForRead[(int)$socket]);
149 | foreach ($tasks as $task) {
150 | $this->schedule($task);
151 | }
152 | }
153 | foreach ($wSocks as $socket) {
154 | list(, $tasks) = $this->waitingForWrite[(int)$socket];
155 | unset($this->waitingForWrite[(int)$socket]);
156 | foreach ($tasks as $task) {
157 | $this->schedule($task);
158 | }
159 | }
160 | }
161 | }
162 |
163 |
164 |
165 |
166 |
167 |
--------------------------------------------------------------------------------
/vendor/composer/InstalledVersions.php:
--------------------------------------------------------------------------------
1 |
7 | * Jordi Boggiano
8 | *
9 | * For the full copyright and license information, please view the LICENSE
10 | * file that was distributed with this source code.
11 | */
12 |
13 | namespace Composer;
14 |
15 | use Composer\Autoload\ClassLoader;
16 | use Composer\Semver\VersionParser;
17 |
18 | /**
19 | * This class is copied in every Composer installed project and available to all
20 | *
21 | * See also https://getcomposer.org/doc/07-runtime.md#installed-versions
22 | *
23 | * To require its presence, you can require `composer-runtime-api ^2.0`
24 | */
25 | class InstalledVersions
26 | {
27 | /**
28 | * @var mixed[]|null
29 | * @psalm-var array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array}|array{}|null
30 | */
31 | private static $installed;
32 |
33 | /**
34 | * @var bool|null
35 | */
36 | private static $canGetVendors;
37 |
38 | /**
39 | * @var array[]
40 | * @psalm-var array}>
41 | */
42 | private static $installedByVendor = array();
43 |
44 | /**
45 | * Returns a list of all package names which are present, either by being installed, replaced or provided
46 | *
47 | * @return string[]
48 | * @psalm-return list
49 | */
50 | public static function getInstalledPackages()
51 | {
52 | $packages = array();
53 | foreach (self::getInstalled() as $installed) {
54 | $packages[] = array_keys($installed['versions']);
55 | }
56 |
57 | if (1 === \count($packages)) {
58 | return $packages[0];
59 | }
60 |
61 | return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
62 | }
63 |
64 | /**
65 | * Returns a list of all package names with a specific type e.g. 'library'
66 | *
67 | * @param string $type
68 | * @return string[]
69 | * @psalm-return list
70 | */
71 | public static function getInstalledPackagesByType($type)
72 | {
73 | $packagesByType = array();
74 |
75 | foreach (self::getInstalled() as $installed) {
76 | foreach ($installed['versions'] as $name => $package) {
77 | if (isset($package['type']) && $package['type'] === $type) {
78 | $packagesByType[] = $name;
79 | }
80 | }
81 | }
82 |
83 | return $packagesByType;
84 | }
85 |
86 | /**
87 | * Checks whether the given package is installed
88 | *
89 | * This also returns true if the package name is provided or replaced by another package
90 | *
91 | * @param string $packageName
92 | * @param bool $includeDevRequirements
93 | * @return bool
94 | */
95 | public static function isInstalled($packageName, $includeDevRequirements = true)
96 | {
97 | foreach (self::getInstalled() as $installed) {
98 | if (isset($installed['versions'][$packageName])) {
99 | return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']);
100 | }
101 | }
102 |
103 | return false;
104 | }
105 |
106 | /**
107 | * Checks whether the given package satisfies a version constraint
108 | *
109 | * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
110 | *
111 | * Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
112 | *
113 | * @param VersionParser $parser Install composer/semver to have access to this class and functionality
114 | * @param string $packageName
115 | * @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
116 | * @return bool
117 | */
118 | public static function satisfies(VersionParser $parser, $packageName, $constraint)
119 | {
120 | $constraint = $parser->parseConstraints($constraint);
121 | $provided = $parser->parseConstraints(self::getVersionRanges($packageName));
122 |
123 | return $provided->matches($constraint);
124 | }
125 |
126 | /**
127 | * Returns a version constraint representing all the range(s) which are installed for a given package
128 | *
129 | * It is easier to use this via isInstalled() with the $constraint argument if you need to check
130 | * whether a given version of a package is installed, and not just whether it exists
131 | *
132 | * @param string $packageName
133 | * @return string Version constraint usable with composer/semver
134 | */
135 | public static function getVersionRanges($packageName)
136 | {
137 | foreach (self::getInstalled() as $installed) {
138 | if (!isset($installed['versions'][$packageName])) {
139 | continue;
140 | }
141 |
142 | $ranges = array();
143 | if (isset($installed['versions'][$packageName]['pretty_version'])) {
144 | $ranges[] = $installed['versions'][$packageName]['pretty_version'];
145 | }
146 | if (array_key_exists('aliases', $installed['versions'][$packageName])) {
147 | $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
148 | }
149 | if (array_key_exists('replaced', $installed['versions'][$packageName])) {
150 | $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
151 | }
152 | if (array_key_exists('provided', $installed['versions'][$packageName])) {
153 | $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
154 | }
155 |
156 | return implode(' || ', $ranges);
157 | }
158 |
159 | throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
160 | }
161 |
162 | /**
163 | * @param string $packageName
164 | * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
165 | */
166 | public static function getVersion($packageName)
167 | {
168 | foreach (self::getInstalled() as $installed) {
169 | if (!isset($installed['versions'][$packageName])) {
170 | continue;
171 | }
172 |
173 | if (!isset($installed['versions'][$packageName]['version'])) {
174 | return null;
175 | }
176 |
177 | return $installed['versions'][$packageName]['version'];
178 | }
179 |
180 | throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
181 | }
182 |
183 | /**
184 | * @param string $packageName
185 | * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
186 | */
187 | public static function getPrettyVersion($packageName)
188 | {
189 | foreach (self::getInstalled() as $installed) {
190 | if (!isset($installed['versions'][$packageName])) {
191 | continue;
192 | }
193 |
194 | if (!isset($installed['versions'][$packageName]['pretty_version'])) {
195 | return null;
196 | }
197 |
198 | return $installed['versions'][$packageName]['pretty_version'];
199 | }
200 |
201 | throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
202 | }
203 |
204 | /**
205 | * @param string $packageName
206 | * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
207 | */
208 | public static function getReference($packageName)
209 | {
210 | foreach (self::getInstalled() as $installed) {
211 | if (!isset($installed['versions'][$packageName])) {
212 | continue;
213 | }
214 |
215 | if (!isset($installed['versions'][$packageName]['reference'])) {
216 | return null;
217 | }
218 |
219 | return $installed['versions'][$packageName]['reference'];
220 | }
221 |
222 | throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
223 | }
224 |
225 | /**
226 | * @param string $packageName
227 | * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
228 | */
229 | public static function getInstallPath($packageName)
230 | {
231 | foreach (self::getInstalled() as $installed) {
232 | if (!isset($installed['versions'][$packageName])) {
233 | continue;
234 | }
235 |
236 | return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
237 | }
238 |
239 | throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
240 | }
241 |
242 | /**
243 | * @return array
244 | * @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}
245 | */
246 | public static function getRootPackage()
247 | {
248 | $installed = self::getInstalled();
249 |
250 | return $installed[0]['root'];
251 | }
252 |
253 | /**
254 | * Returns the raw installed.php data for custom implementations
255 | *
256 | * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
257 | * @return array[]
258 | * @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array}
259 | */
260 | public static function getRawData()
261 | {
262 | @trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
263 |
264 | if (null === self::$installed) {
265 | // only require the installed.php file if this file is loaded from its dumped location,
266 | // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
267 | if (substr(__DIR__, -8, 1) !== 'C') {
268 | self::$installed = include __DIR__ . '/installed.php';
269 | } else {
270 | self::$installed = array();
271 | }
272 | }
273 |
274 | return self::$installed;
275 | }
276 |
277 | /**
278 | * Returns the raw data of all installed.php which are currently loaded for custom implementations
279 | *
280 | * @return array[]
281 | * @psalm-return list}>
282 | */
283 | public static function getAllRawData()
284 | {
285 | return self::getInstalled();
286 | }
287 |
288 | /**
289 | * Lets you reload the static array from another file
290 | *
291 | * This is only useful for complex integrations in which a project needs to use
292 | * this class but then also needs to execute another project's autoloader in process,
293 | * and wants to ensure both projects have access to their version of installed.php.
294 | *
295 | * A typical case would be PHPUnit, where it would need to make sure it reads all
296 | * the data it needs from this class, then call reload() with
297 | * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
298 | * the project in which it runs can then also use this class safely, without
299 | * interference between PHPUnit's dependencies and the project's dependencies.
300 | *
301 | * @param array[] $data A vendor/composer/installed.php data set
302 | * @return void
303 | *
304 | * @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array} $data
305 | */
306 | public static function reload($data)
307 | {
308 | self::$installed = $data;
309 | self::$installedByVendor = array();
310 | }
311 |
312 | /**
313 | * @return array[]
314 | * @psalm-return list}>
315 | */
316 | private static function getInstalled()
317 | {
318 | if (null === self::$canGetVendors) {
319 | self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
320 | }
321 |
322 | $installed = array();
323 |
324 | if (self::$canGetVendors) {
325 | foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
326 | if (isset(self::$installedByVendor[$vendorDir])) {
327 | $installed[] = self::$installedByVendor[$vendorDir];
328 | } elseif (is_file($vendorDir.'/composer/installed.php')) {
329 | $installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
330 | if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
331 | self::$installed = $installed[count($installed) - 1];
332 | }
333 | }
334 | }
335 | }
336 |
337 | if (null === self::$installed) {
338 | // only require the installed.php file if this file is loaded from its dumped location,
339 | // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
340 | if (substr(__DIR__, -8, 1) !== 'C') {
341 | self::$installed = require __DIR__ . '/installed.php';
342 | } else {
343 | self::$installed = array();
344 | }
345 | }
346 | $installed[] = self::$installed;
347 |
348 | return $installed;
349 | }
350 | }
351 |
--------------------------------------------------------------------------------
/vendor/composer/ClassLoader.php:
--------------------------------------------------------------------------------
1 |
7 | * Jordi Boggiano
8 | *
9 | * For the full copyright and license information, please view the LICENSE
10 | * file that was distributed with this source code.
11 | */
12 |
13 | namespace Composer\Autoload;
14 |
15 | /**
16 | * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
17 | *
18 | * $loader = new \Composer\Autoload\ClassLoader();
19 | *
20 | * // register classes with namespaces
21 | * $loader->add('Symfony\Component', __DIR__.'/component');
22 | * $loader->add('Symfony', __DIR__.'/framework');
23 | *
24 | * // activate the autoloader
25 | * $loader->register();
26 | *
27 | * // to enable searching the include path (eg. for PEAR packages)
28 | * $loader->setUseIncludePath(true);
29 | *
30 | * In this example, if you try to use a class in the Symfony\Component
31 | * namespace or one of its children (Symfony\Component\Console for instance),
32 | * the autoloader will first look for the class under the component/
33 | * directory, and it will then fallback to the framework/ directory if not
34 | * found before giving up.
35 | *
36 | * This class is loosely based on the Symfony UniversalClassLoader.
37 | *
38 | * @author Fabien Potencier
39 | * @author Jordi Boggiano
40 | * @see https://www.php-fig.org/psr/psr-0/
41 | * @see https://www.php-fig.org/psr/psr-4/
42 | */
43 | class ClassLoader
44 | {
45 | /** @var ?string */
46 | private $vendorDir;
47 |
48 | // PSR-4
49 | /**
50 | * @var array[]
51 | * @psalm-var array>
52 | */
53 | private $prefixLengthsPsr4 = array();
54 | /**
55 | * @var array[]
56 | * @psalm-var array>
57 | */
58 | private $prefixDirsPsr4 = array();
59 | /**
60 | * @var array[]
61 | * @psalm-var array
62 | */
63 | private $fallbackDirsPsr4 = array();
64 |
65 | // PSR-0
66 | /**
67 | * @var array[]
68 | * @psalm-var array>
69 | */
70 | private $prefixesPsr0 = array();
71 | /**
72 | * @var array[]
73 | * @psalm-var array
74 | */
75 | private $fallbackDirsPsr0 = array();
76 |
77 | /** @var bool */
78 | private $useIncludePath = false;
79 |
80 | /**
81 | * @var string[]
82 | * @psalm-var array
83 | */
84 | private $classMap = array();
85 |
86 | /** @var bool */
87 | private $classMapAuthoritative = false;
88 |
89 | /**
90 | * @var bool[]
91 | * @psalm-var array
92 | */
93 | private $missingClasses = array();
94 |
95 | /** @var ?string */
96 | private $apcuPrefix;
97 |
98 | /**
99 | * @var self[]
100 | */
101 | private static $registeredLoaders = array();
102 |
103 | /**
104 | * @param ?string $vendorDir
105 | */
106 | public function __construct($vendorDir = null)
107 | {
108 | $this->vendorDir = $vendorDir;
109 | }
110 |
111 | /**
112 | * @return string[]
113 | */
114 | public function getPrefixes()
115 | {
116 | if (!empty($this->prefixesPsr0)) {
117 | return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
118 | }
119 |
120 | return array();
121 | }
122 |
123 | /**
124 | * @return array[]
125 | * @psalm-return array>
126 | */
127 | public function getPrefixesPsr4()
128 | {
129 | return $this->prefixDirsPsr4;
130 | }
131 |
132 | /**
133 | * @return array[]
134 | * @psalm-return array
135 | */
136 | public function getFallbackDirs()
137 | {
138 | return $this->fallbackDirsPsr0;
139 | }
140 |
141 | /**
142 | * @return array[]
143 | * @psalm-return array
144 | */
145 | public function getFallbackDirsPsr4()
146 | {
147 | return $this->fallbackDirsPsr4;
148 | }
149 |
150 | /**
151 | * @return string[] Array of classname => path
152 | * @psalm-return array
153 | */
154 | public function getClassMap()
155 | {
156 | return $this->classMap;
157 | }
158 |
159 | /**
160 | * @param string[] $classMap Class to filename map
161 | * @psalm-param array $classMap
162 | *
163 | * @return void
164 | */
165 | public function addClassMap(array $classMap)
166 | {
167 | if ($this->classMap) {
168 | $this->classMap = array_merge($this->classMap, $classMap);
169 | } else {
170 | $this->classMap = $classMap;
171 | }
172 | }
173 |
174 | /**
175 | * Registers a set of PSR-0 directories for a given prefix, either
176 | * appending or prepending to the ones previously set for this prefix.
177 | *
178 | * @param string $prefix The prefix
179 | * @param string[]|string $paths The PSR-0 root directories
180 | * @param bool $prepend Whether to prepend the directories
181 | *
182 | * @return void
183 | */
184 | public function add($prefix, $paths, $prepend = false)
185 | {
186 | if (!$prefix) {
187 | if ($prepend) {
188 | $this->fallbackDirsPsr0 = array_merge(
189 | (array) $paths,
190 | $this->fallbackDirsPsr0
191 | );
192 | } else {
193 | $this->fallbackDirsPsr0 = array_merge(
194 | $this->fallbackDirsPsr0,
195 | (array) $paths
196 | );
197 | }
198 |
199 | return;
200 | }
201 |
202 | $first = $prefix[0];
203 | if (!isset($this->prefixesPsr0[$first][$prefix])) {
204 | $this->prefixesPsr0[$first][$prefix] = (array) $paths;
205 |
206 | return;
207 | }
208 | if ($prepend) {
209 | $this->prefixesPsr0[$first][$prefix] = array_merge(
210 | (array) $paths,
211 | $this->prefixesPsr0[$first][$prefix]
212 | );
213 | } else {
214 | $this->prefixesPsr0[$first][$prefix] = array_merge(
215 | $this->prefixesPsr0[$first][$prefix],
216 | (array) $paths
217 | );
218 | }
219 | }
220 |
221 | /**
222 | * Registers a set of PSR-4 directories for a given namespace, either
223 | * appending or prepending to the ones previously set for this namespace.
224 | *
225 | * @param string $prefix The prefix/namespace, with trailing '\\'
226 | * @param string[]|string $paths The PSR-4 base directories
227 | * @param bool $prepend Whether to prepend the directories
228 | *
229 | * @throws \InvalidArgumentException
230 | *
231 | * @return void
232 | */
233 | public function addPsr4($prefix, $paths, $prepend = false)
234 | {
235 | if (!$prefix) {
236 | // Register directories for the root namespace.
237 | if ($prepend) {
238 | $this->fallbackDirsPsr4 = array_merge(
239 | (array) $paths,
240 | $this->fallbackDirsPsr4
241 | );
242 | } else {
243 | $this->fallbackDirsPsr4 = array_merge(
244 | $this->fallbackDirsPsr4,
245 | (array) $paths
246 | );
247 | }
248 | } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
249 | // Register directories for a new namespace.
250 | $length = strlen($prefix);
251 | if ('\\' !== $prefix[$length - 1]) {
252 | throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
253 | }
254 | $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
255 | $this->prefixDirsPsr4[$prefix] = (array) $paths;
256 | } elseif ($prepend) {
257 | // Prepend directories for an already registered namespace.
258 | $this->prefixDirsPsr4[$prefix] = array_merge(
259 | (array) $paths,
260 | $this->prefixDirsPsr4[$prefix]
261 | );
262 | } else {
263 | // Append directories for an already registered namespace.
264 | $this->prefixDirsPsr4[$prefix] = array_merge(
265 | $this->prefixDirsPsr4[$prefix],
266 | (array) $paths
267 | );
268 | }
269 | }
270 |
271 | /**
272 | * Registers a set of PSR-0 directories for a given prefix,
273 | * replacing any others previously set for this prefix.
274 | *
275 | * @param string $prefix The prefix
276 | * @param string[]|string $paths The PSR-0 base directories
277 | *
278 | * @return void
279 | */
280 | public function set($prefix, $paths)
281 | {
282 | if (!$prefix) {
283 | $this->fallbackDirsPsr0 = (array) $paths;
284 | } else {
285 | $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
286 | }
287 | }
288 |
289 | /**
290 | * Registers a set of PSR-4 directories for a given namespace,
291 | * replacing any others previously set for this namespace.
292 | *
293 | * @param string $prefix The prefix/namespace, with trailing '\\'
294 | * @param string[]|string $paths The PSR-4 base directories
295 | *
296 | * @throws \InvalidArgumentException
297 | *
298 | * @return void
299 | */
300 | public function setPsr4($prefix, $paths)
301 | {
302 | if (!$prefix) {
303 | $this->fallbackDirsPsr4 = (array) $paths;
304 | } else {
305 | $length = strlen($prefix);
306 | if ('\\' !== $prefix[$length - 1]) {
307 | throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
308 | }
309 | $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
310 | $this->prefixDirsPsr4[$prefix] = (array) $paths;
311 | }
312 | }
313 |
314 | /**
315 | * Turns on searching the include path for class files.
316 | *
317 | * @param bool $useIncludePath
318 | *
319 | * @return void
320 | */
321 | public function setUseIncludePath($useIncludePath)
322 | {
323 | $this->useIncludePath = $useIncludePath;
324 | }
325 |
326 | /**
327 | * Can be used to check if the autoloader uses the include path to check
328 | * for classes.
329 | *
330 | * @return bool
331 | */
332 | public function getUseIncludePath()
333 | {
334 | return $this->useIncludePath;
335 | }
336 |
337 | /**
338 | * Turns off searching the prefix and fallback directories for classes
339 | * that have not been registered with the class map.
340 | *
341 | * @param bool $classMapAuthoritative
342 | *
343 | * @return void
344 | */
345 | public function setClassMapAuthoritative($classMapAuthoritative)
346 | {
347 | $this->classMapAuthoritative = $classMapAuthoritative;
348 | }
349 |
350 | /**
351 | * Should class lookup fail if not found in the current class map?
352 | *
353 | * @return bool
354 | */
355 | public function isClassMapAuthoritative()
356 | {
357 | return $this->classMapAuthoritative;
358 | }
359 |
360 | /**
361 | * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
362 | *
363 | * @param string|null $apcuPrefix
364 | *
365 | * @return void
366 | */
367 | public function setApcuPrefix($apcuPrefix)
368 | {
369 | $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
370 | }
371 |
372 | /**
373 | * The APCu prefix in use, or null if APCu caching is not enabled.
374 | *
375 | * @return string|null
376 | */
377 | public function getApcuPrefix()
378 | {
379 | return $this->apcuPrefix;
380 | }
381 |
382 | /**
383 | * Registers this instance as an autoloader.
384 | *
385 | * @param bool $prepend Whether to prepend the autoloader or not
386 | *
387 | * @return void
388 | */
389 | public function register($prepend = false)
390 | {
391 | spl_autoload_register(array($this, 'loadClass'), true, $prepend);
392 |
393 | if (null === $this->vendorDir) {
394 | return;
395 | }
396 |
397 | if ($prepend) {
398 | self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
399 | } else {
400 | unset(self::$registeredLoaders[$this->vendorDir]);
401 | self::$registeredLoaders[$this->vendorDir] = $this;
402 | }
403 | }
404 |
405 | /**
406 | * Unregisters this instance as an autoloader.
407 | *
408 | * @return void
409 | */
410 | public function unregister()
411 | {
412 | spl_autoload_unregister(array($this, 'loadClass'));
413 |
414 | if (null !== $this->vendorDir) {
415 | unset(self::$registeredLoaders[$this->vendorDir]);
416 | }
417 | }
418 |
419 | /**
420 | * Loads the given class or interface.
421 | *
422 | * @param string $class The name of the class
423 | * @return true|null True if loaded, null otherwise
424 | */
425 | public function loadClass($class)
426 | {
427 | if ($file = $this->findFile($class)) {
428 | includeFile($file);
429 |
430 | return true;
431 | }
432 |
433 | return null;
434 | }
435 |
436 | /**
437 | * Finds the path to the file where the class is defined.
438 | *
439 | * @param string $class The name of the class
440 | *
441 | * @return string|false The path if found, false otherwise
442 | */
443 | public function findFile($class)
444 | {
445 | // class map lookup
446 | if (isset($this->classMap[$class])) {
447 | return $this->classMap[$class];
448 | }
449 | if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
450 | return false;
451 | }
452 | if (null !== $this->apcuPrefix) {
453 | $file = apcu_fetch($this->apcuPrefix.$class, $hit);
454 | if ($hit) {
455 | return $file;
456 | }
457 | }
458 |
459 | $file = $this->findFileWithExtension($class, '.php');
460 |
461 | // Search for Hack files if we are running on HHVM
462 | if (false === $file && defined('HHVM_VERSION')) {
463 | $file = $this->findFileWithExtension($class, '.hh');
464 | }
465 |
466 | if (null !== $this->apcuPrefix) {
467 | apcu_add($this->apcuPrefix.$class, $file);
468 | }
469 |
470 | if (false === $file) {
471 | // Remember that this class does not exist.
472 | $this->missingClasses[$class] = true;
473 | }
474 |
475 | return $file;
476 | }
477 |
478 | /**
479 | * Returns the currently registered loaders indexed by their corresponding vendor directories.
480 | *
481 | * @return self[]
482 | */
483 | public static function getRegisteredLoaders()
484 | {
485 | return self::$registeredLoaders;
486 | }
487 |
488 | /**
489 | * @param string $class
490 | * @param string $ext
491 | * @return string|false
492 | */
493 | private function findFileWithExtension($class, $ext)
494 | {
495 | // PSR-4 lookup
496 | $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
497 |
498 | $first = $class[0];
499 | if (isset($this->prefixLengthsPsr4[$first])) {
500 | $subPath = $class;
501 | while (false !== $lastPos = strrpos($subPath, '\\')) {
502 | $subPath = substr($subPath, 0, $lastPos);
503 | $search = $subPath . '\\';
504 | if (isset($this->prefixDirsPsr4[$search])) {
505 | $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
506 | foreach ($this->prefixDirsPsr4[$search] as $dir) {
507 | if (file_exists($file = $dir . $pathEnd)) {
508 | return $file;
509 | }
510 | }
511 | }
512 | }
513 | }
514 |
515 | // PSR-4 fallback dirs
516 | foreach ($this->fallbackDirsPsr4 as $dir) {
517 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
518 | return $file;
519 | }
520 | }
521 |
522 | // PSR-0 lookup
523 | if (false !== $pos = strrpos($class, '\\')) {
524 | // namespaced class name
525 | $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
526 | . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
527 | } else {
528 | // PEAR-like class name
529 | $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
530 | }
531 |
532 | if (isset($this->prefixesPsr0[$first])) {
533 | foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
534 | if (0 === strpos($class, $prefix)) {
535 | foreach ($dirs as $dir) {
536 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
537 | return $file;
538 | }
539 | }
540 | }
541 | }
542 | }
543 |
544 | // PSR-0 fallback dirs
545 | foreach ($this->fallbackDirsPsr0 as $dir) {
546 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
547 | return $file;
548 | }
549 | }
550 |
551 | // PSR-0 include paths.
552 | if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
553 | return $file;
554 | }
555 |
556 | return false;
557 | }
558 | }
559 |
560 | /**
561 | * Scope isolated include.
562 | *
563 | * Prevents access to $this/self from included files.
564 | *
565 | * @param string $file
566 | * @return void
567 | * @private
568 | */
569 | function includeFile($file)
570 | {
571 | include $file;
572 | }
573 |
--------------------------------------------------------------------------------