├── logo.png
├── logo-icon.png
├── .gitignore
├── README.md
├── src
├── Exception.php
├── TypeException.php
├── LoopInitializedException.php
├── Timers
│ ├── Timer.php
│ └── TimerDevice.php
├── EventDeviceInterface.php
├── PosixSignalEventDevice.php
├── functions.php
├── LoopInterface.php
├── EventDeviceBinder.php
├── StreamEventDevice.php
├── Loop.php
└── NativeLoop.php
├── CONTRIBUTING.md
├── composer.json
├── phpunit.xml.dist
├── tests
├── EventDeviceBinderTest.php
├── LoopTest.php
└── NativeLoopTest.php
├── .scrutinizer.yml
├── LICENSE.md
└── logo.svg
/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sagebind/evflow/HEAD/logo.png
--------------------------------------------------------------------------------
/logo-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sagebind/evflow/HEAD/logo-icon.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # ignore dependencies
2 | vendor/
3 |
4 | # ignore lock file
5 | composer.lock
6 |
7 | # ignore api documentation cache and build files
8 | build/
9 | cache/
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 
2 |
3 | [](http://www.apache.org/licenses/LICENSE-2.0)
4 | [](https://scrutinizer-ci.com/g/evflow/evflow/issues/master)
5 |
6 | **Notice: This project has been superceded by [Icicle](http://github.com/icicleio), a collaborative effort to bring asynchronous and multithreaded processing to PHP. Please check out Icicle instead.**
7 |
8 | ## License
9 | All Evflow documentation and source code is licensed under the Apache License, Version 2.0 (Apache-2.0). See [LICENSE.md](LICENSE.md) for details.
10 |
--------------------------------------------------------------------------------
/src/Exception.php:
--------------------------------------------------------------------------------
1 |
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not
6 | * use this file except in compliance with the License. You may obtain a copy
7 | * of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 | * License for the specific language governing permissions and limitations
15 | * under the License.
16 | */
17 |
18 | namespace Evflow;
19 |
20 | /**
21 | * Package-level exception interface.
22 | */
23 | interface Exception
24 | {
25 | }
26 |
--------------------------------------------------------------------------------
/src/TypeException.php:
--------------------------------------------------------------------------------
1 |
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not
6 | * use this file except in compliance with the License. You may obtain a copy
7 | * of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 | * License for the specific language governing permissions and limitations
15 | * under the License.
16 | */
17 |
18 | namespace Evflow;
19 |
20 | class TypeException extends \InvalidArgumentException implements Exception
21 | {
22 | }
23 |
--------------------------------------------------------------------------------
/src/LoopInitializedException.php:
--------------------------------------------------------------------------------
1 |
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not
6 | * use this file except in compliance with the License. You may obtain a copy
7 | * of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 | * License for the specific language governing permissions and limitations
15 | * under the License.
16 | */
17 |
18 | namespace Evflow;
19 |
20 | class LoopInitializedException extends \LogicException implements Exception
21 | {
22 | }
23 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to Evflow
2 | First, thanks for wanting to contribute to Evflow! This project would be nothing
3 | without you and the amazing PHP community.
4 |
5 | ## What to help out with
6 | Evflow is still in an early stage at the moment, so most of the contributions
7 | will be related to design and architecture rather than code. These documents can
8 | be found and contributed to in the [docs](http://github.com/evflow/docs)
9 | repository.
10 |
11 | ## Pull requests
12 | 1. Fork the main Evflow repository
13 | 2. Make your changes
14 | 3. Send a pull request to the master branch
15 |
16 | ## Style guide
17 | All pull requests must adhere to the [PSR-2 standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md).
18 |
19 | ## Licensing
20 | All code contributed will be licensed under Evflow's Apache license, in
21 | accordance with section 5 of the license. See [LICENSE.md](LICENSE.md) for
22 | details.
23 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "evflow/evflow",
3 | "description": "Open and extensible event loop library for asynchronous programming in PHP",
4 | "type": "library",
5 | "license": "Apache-2.0",
6 | "authors": [
7 | {
8 | "name": "Stephen Coakley",
9 | "email": "me@stephencoakley.com",
10 | "homepage": "http://stephencoakley.com"
11 | }
12 | ],
13 | "require": {
14 | "evenement/evenement": "2.0.*",
15 | "php": ">=5.5.0",
16 | "psr/log": "~1.0",
17 | "react/promise": "~2.1"
18 | },
19 | "require-dev": {
20 | "monolog/monolog": "~1.12",
21 | "phpunit/phpunit": "~4.0"
22 | },
23 | "autoload": {
24 | "psr-4": {
25 | "Evflow\\": "src"
26 | },
27 | "files": [
28 | "src/functions.php"
29 | ]
30 | },
31 | "autoload-dev": {
32 | "psr-4": {
33 | "Evflow\\Tests\\": "tests"
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
14 | tests
15 |
16 |
17 |
18 |
19 | src
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/tests/EventDeviceBinderTest.php:
--------------------------------------------------------------------------------
1 |
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not
6 | * use this file except in compliance with the License. You may obtain a copy
7 | * of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 | * License for the specific language governing permissions and limitations
15 | * under the License.
16 | */
17 |
18 | namespace Evflow\Tests;
19 |
20 | use Evflow\EventDeviceBinder;
21 | use Evflow\EventDeviceInterface;
22 |
23 | class EventDeviceBinderTest extends \PHPUnit_Framework_TestCase
24 | {
25 | public function setUp()
26 | {
27 | $this->manager = new EventDeviceBinder();
28 | }
29 |
30 | public function testBindDevice()
31 | {
32 | $this->manager->bindDevice($this->getMock(EventDeviceInterface::class));
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/Timers/Timer.php:
--------------------------------------------------------------------------------
1 |
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not
6 | * use this file except in compliance with the License. You may obtain a copy
7 | * of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 | * License for the specific language governing permissions and limitations
15 | * under the License.
16 | */
17 |
18 | namespace Evflow\Timers;
19 |
20 | use Evflow\DefaultLoop;
21 | use Evflow\LoopInterface;
22 |
23 | class Timer
24 | {
25 | protected $interval;
26 | protected $periodic;
27 | protected $callback;
28 |
29 | public function __construct($interval, callable $callback, $periodic = false, LoopInterface $loop = null)
30 | {
31 | $this->interval = $interval;
32 | $this->periodic = !!$periodic;
33 | $this->callback = $callback;
34 | $loop = $loop !== null ? $loop : DefaultLoop::instance();
35 | $loop->fetchDevice(TimerDevice::class)->addTimer($this);
36 | }
37 |
38 | public function isPeriodic()
39 | {
40 | return $this->periodic;
41 | }
42 |
43 | public function getInterval()
44 | {
45 | return $this->interval;
46 | }
47 |
48 | public function getCallback()
49 | {
50 | return $this->callback;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/EventDeviceInterface.php:
--------------------------------------------------------------------------------
1 |
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not
6 | * use this file except in compliance with the License. You may obtain a copy
7 | * of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 | * License for the specific language governing permissions and limitations
15 | * under the License.
16 | */
17 |
18 | namespace Evflow;
19 |
20 | /**
21 | * An interface for event devices that can be polled by an event loop.
22 | */
23 | interface EventDeviceInterface
24 | {
25 | /**
26 | * Polls the event device to process new incoming events.
27 | *
28 | * The event device should wait for new events until `$timeout` is reached.
29 | * If a 0 is given as the timeout, the event device should check for events
30 | * once and return immediately. If a timeout of -1 is given, the event device
31 | * should wait indefinitely for new events until at least one occurs.
32 | *
33 | * @param LoopInterface $loop The event loop context of the poll.
34 | * @param int $timeout The poll timeout in microseconds.
35 | */
36 | public function poll(LoopInterface $loop, $timeout);
37 |
38 | /**
39 | * Checks if the event device is actively listening for events.
40 | *
41 | * An event device should be active if it still has more events that will
42 | * trigger any callbacks. That is, this function should return false only
43 | * if it is no longer possible for the event device to generate new tasks
44 | * to be scheduled.
45 | *
46 | * @return bool True if the event device is idle, otherwise false.
47 | */
48 | public function isActive();
49 | }
50 |
--------------------------------------------------------------------------------
/src/PosixSignalEventDevice.php:
--------------------------------------------------------------------------------
1 |
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not
6 | * use this file except in compliance with the License. You may obtain a copy
7 | * of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 | * License for the specific language governing permissions and limitations
15 | * under the License.
16 | */
17 |
18 | namespace Evflow;
19 |
20 | /**
21 | * Event device that captures POSIX process signals.
22 | *
23 | * @todo Old and messed up. Needs a-fixin'.
24 | */
25 | class PosixSignalEventDevice implements EventDeviceInterface
26 | {
27 | protected static $signalRefCount = [];
28 |
29 | protected $signal;
30 |
31 | public function __construct($signal, LoopInterface $loop = null)
32 | {
33 | parent::__construct($loop);
34 | $this->signal = $signal;
35 | $this->incrementRefCount($signal);
36 | }
37 |
38 | public function poll($timeout)
39 | {
40 | return pcntl_sigtimedwait([$this->signal], $siginfo, 0, 0) === $this->signal;
41 | }
42 |
43 | public function __destruct()
44 | {
45 | $this->decrementRefCount($this->signal);
46 | }
47 |
48 | protected function getRefCount($signal)
49 | {
50 | if (isset(self::$signalRefCount[$signal])) {
51 | return self::$signalRefCount[$signal];
52 | } else {
53 | return 0;
54 | }
55 | }
56 |
57 | protected function incrementRefCount($signal)
58 | {
59 | self::$signalRefCount[$signal] = $this->getRefCount($signal) + 1;
60 |
61 | if ($this->getRefCount($signal) === 1) {
62 | pcntl_sigprocmask(SIG_BLOCK, [$signal]);
63 | }
64 | }
65 |
66 | protected function decrementRefCount($signal)
67 | {
68 | if ($this->getRefCount($signal) > 0) {
69 | self::$signalRefCount[$signal] = $this->getRefCount($signal) - 1;
70 | }
71 |
72 | if ($this->getRefCount($signal) === 0) {
73 | pcntl_sigprocmask(SIG_UNBLOCK, [$signal]);
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/.scrutinizer.yml:
--------------------------------------------------------------------------------
1 | build:
2 | tests:
3 | override:
4 | -
5 | command: phpunit --coverage-clover=coverage.clover
6 | coverage:
7 | file: coverage.clover
8 | format: php-clover
9 |
10 | filter:
11 | excluded_paths:
12 | - tests/*
13 |
14 | checks:
15 | php:
16 | code_rating: true
17 | duplication: true
18 | variable_existence: true
19 | uppercase_constants: true
20 | unused_variables: true
21 | unreachable_code: true
22 | unused_methods: true
23 | unused_parameters: true
24 | unused_properties: true
25 | return_doc_comments: true
26 | require_scope_for_properties: true
27 | require_scope_for_methods: true
28 | require_php_tag_first: true
29 | precedence_in_conditions: true
30 | precedence_mistakes: true
31 | phpunit_assertions: true
32 | php5_style_constructor: true
33 | parameter_non_unique: true
34 | parameter_doc_comments: true
35 | no_non_implemented_abstract_methods: true
36 | no_debug_code: true
37 | useless_calls: true
38 | use_statement_alias_conflict: true
39 | sql_injection_vulnerabilities: true
40 | security_vulnerabilities: true
41 | no_property_on_interface: true
42 | deprecated_code_usage: true
43 | closure_use_not_conflicting: true
44 | closure_use_modifiable: true
45 | avoid_useless_overridden_methods: true
46 | avoid_conflicting_incrementers: true
47 | assignment_of_null_return: true
48 | argument_type_checks: true
49 | avoid_tab_indentation: true
50 | avoid_usage_of_logical_operators: true
51 | blank_line_after_namespace_declaration: true
52 | classes_in_camel_caps: true
53 | encourage_single_quotes: true
54 | foreach_traversable: true
55 | lowercase_basic_constants: true
56 | lowercase_php_keywords: true
57 | instanceof_class_exists: true
58 | function_in_camel_caps: true
59 | newline_at_end_of_file: true
60 | no_else_if_statements: true
61 | no_trailing_whitespace: true
62 | no_unnecessary_function_call_in_for_loop: true
63 | properties_in_camelcaps: true
64 |
65 | tools:
66 | php_changetracking:
67 | enabled: true
68 | bug_patterns:
69 | - '\bfix(?:es|ed)?\b'
70 | feature_patterns:
71 | - '\badd(?:s|ed)?\b'
72 | - '\bimplement(?:s|ed)?\b'
73 | filter:
74 | paths: { }
75 | excluded_paths: { }
76 |
--------------------------------------------------------------------------------
/src/functions.php:
--------------------------------------------------------------------------------
1 |
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not
6 | * use this file except in compliance with the License. You may obtain a copy
7 | * of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 | * License for the specific language governing permissions and limitations
15 | * under the License.
16 | */
17 |
18 | namespace Evflow;
19 |
20 | use React\Promise\FulfilledPromise;
21 | use React\Promise\Promise;
22 | use React\Promise\PromiseInterface;
23 |
24 | /**
25 | * Creates and returns a promise to call a generator function asynchronously.
26 | *
27 | * @param callable $function
28 | *
29 | * @return Promise
30 | */
31 | function async(callable $function)
32 | {
33 | // return a new promise
34 | return new Promise(function (callable $resolve, callable $reject, callable $progress) use ($function) {
35 | // get the generator
36 | $generator = $function();
37 |
38 | // function to step the generator to the next yield statement
39 | $step = function () use (&$step, $generator, $resolve, $reject, $progress) {
40 | $value = null;
41 |
42 | // try to execute the next block of code
43 | try {
44 | $value = $generator->current();
45 | } catch (\Exception $exception) {
46 | // exception rejects the promise
47 | $reject($exception);
48 |
49 | return;
50 | }
51 |
52 | // if the generator is complete, resolve the promise
53 | if (!$generator->valid()) {
54 | $resolve($value);
55 |
56 | return;
57 | }
58 |
59 | // wrap the value in a promise if it isn't already one
60 | if (!($value instanceof PromiseInterface)) {
61 | $value = new FulfilledPromise($value);
62 | }
63 |
64 | // run the next step when the current one resolves
65 | $value->then(function ($value) use (&$step, $generator) {
66 | $generator->send($value);
67 | $step();
68 | }, function (\Exception $reason) use ($generator) {
69 | $generator->throw($reason);
70 | });
71 | };
72 |
73 | // run the first step in the next tick
74 | DefaultLoop::instance()->nextTick($step);
75 | });
76 | }
77 |
--------------------------------------------------------------------------------
/tests/LoopTest.php:
--------------------------------------------------------------------------------
1 |
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not
6 | * use this file except in compliance with the License. You may obtain a copy
7 | * of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 | * License for the specific language governing permissions and limitations
15 | * under the License.
16 | */
17 |
18 | namespace Evflow\Tests;
19 |
20 | use Evflow\Loop;
21 | use Evflow\LoopInterface;
22 |
23 | /**
24 | * @runTestsInSeparateProcesses
25 | * @preserveGlobalState disabled
26 | */
27 | class LoopTest extends \PHPUnit_Framework_TestCase
28 | {
29 | /**
30 | * @var LoopInterface A mock loop instance.
31 | */
32 | protected $loop;
33 |
34 | public function setUp()
35 | {
36 | $this->loop = $this->getMockBuilder(LoopInterface::class)
37 | ->getMock();
38 | }
39 |
40 | public function testInstanceReturnsInstance()
41 | {
42 | Loop::init($this->loop);
43 | $this->assertSame($this->loop, Loop::instance());
44 | }
45 |
46 | /**
47 | * @expectedException Evflow\LoopInitializedException
48 | */
49 | public function testInitCanOnlyBeCalledOnce()
50 | {
51 | Loop::init($this->loop);
52 | Loop::init($this->loop);
53 | }
54 |
55 | public function testInitCalledAutomatically()
56 | {
57 | $this->assertInstanceOf(LoopInterface::class, Loop::instance());
58 | }
59 |
60 | public function testIsRunningIsCalled()
61 | {
62 | $this->loop->expects($this->once())
63 | ->method('isRunning');
64 |
65 | Loop::init($this->loop);
66 | Loop::isRunning();
67 | }
68 |
69 | public function testTickIsCalled()
70 | {
71 | $this->loop->expects($this->once())
72 | ->method('tick');
73 |
74 | Loop::init($this->loop);
75 | Loop::tick();
76 | }
77 |
78 | public function testRunIsCalled()
79 | {
80 | $this->loop->expects($this->once())
81 | ->method('run');
82 |
83 | Loop::init($this->loop);
84 | Loop::run();
85 | }
86 |
87 | public function testStopIsCalled()
88 | {
89 | $this->loop->expects($this->once())
90 | ->method('stop');
91 |
92 | Loop::init($this->loop);
93 | Loop::stop();
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/tests/NativeLoopTest.php:
--------------------------------------------------------------------------------
1 |
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not
6 | * use this file except in compliance with the License. You may obtain a copy
7 | * of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 | * License for the specific language governing permissions and limitations
15 | * under the License.
16 | */
17 |
18 | namespace Evflow\Tests;
19 |
20 | use Evflow\NativeLoop;
21 | use Evflow\StreamEventDevice;
22 | use Evflow\Timers\Timer;
23 | use Evflow\Timers\TimerDevice;
24 | use Monolog\Handler\StreamHandler;
25 | use Monolog\Logger;
26 |
27 | class NativeLoopTest extends \PHPUnit_Framework_TestCase
28 | {
29 | /**
30 | * @var NativeLoop A native loop instance to test.
31 | */
32 | protected $loop;
33 |
34 | public function setUp()
35 | {
36 | fwrite(STDOUT, PHP_EOL);
37 |
38 | // set up logging so we can ses what is going on
39 | $log = new Logger('EventLoop');
40 | $log->pushHandler(new StreamHandler('php://stdout', Logger::DEBUG));
41 |
42 | // create a loop instance and set the logger
43 | $this->loop = new NativeLoop();
44 | $this->loop->setLogger($log);
45 | }
46 |
47 | public function testTimer()
48 | {
49 | // bind the timer device
50 | $this->loop->bindDevice(new TimerDevice);
51 | $ran = false;
52 |
53 | $timer = new Timer(1000000, function () use (&$ran) {
54 | $ran = !$ran;
55 | }, false, $this->loop);
56 |
57 | $this->loop->run();
58 | $this->assertTrue($ran);
59 | }
60 |
61 | public function testStreams()
62 | {
63 | $this->loop->bindDevice(new StreamEventDevice);
64 | $device = $this->loop->fetchDevice(StreamEventDevice::class);
65 |
66 | $ran = false;
67 | // open a socket
68 | $socket = fsockopen('fake-response.appspot.com', 80, $errno, $errstr, 30);
69 |
70 | // send an HTTP request
71 | $out = "GET / HTTP/1.1\r\n";
72 | $out .= "Host: fake-response.appspot.com\r\n";
73 | $out .= "Connection: Close\r\n\r\n";
74 | fwrite($socket, $out);
75 |
76 | $device->addStream($socket, StreamEventDevice::READ, function ($stream) use (&$ran) {
77 | fread($stream, 1024);
78 | fclose($stream);
79 | $ran = true;
80 | });
81 |
82 | $this->loop->run();
83 | $this->assertTrue($ran);
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/LoopInterface.php:
--------------------------------------------------------------------------------
1 |
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not
6 | * use this file except in compliance with the License. You may obtain a copy
7 | * of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 | * License for the specific language governing permissions and limitations
15 | * under the License.
16 | */
17 |
18 | namespace Evflow;
19 |
20 | /**
21 | * A generic loop interface that supports execution control and a task double queue.
22 | */
23 | interface LoopInterface
24 | {
25 | /**
26 | * Binds an event device instance to a type, or to itself if no
27 | * interface is specified.
28 | *
29 | * @param EventDeviceInterface $instance The object instance to bind.
30 | * @param string $type The type to bind to.
31 | */
32 | public function bindDevice(EventDeviceInterface $instance, $type = null);
33 |
34 | /**
35 | * Fetches an event device bound to a given type.
36 | *
37 | * @param string $type The type name to fetch.
38 | *
39 | * @return EventDeviceInterface The device instance bound to the given type.
40 | */
41 | public function fetchDevice($type);
42 |
43 | /**
44 | * Schedules a callback to be executed in the future.
45 | *
46 | * This function is typically used to queue up callbacks for asynchronous
47 | * events, usually from an event device.
48 | *
49 | * @param callable $callback
50 | */
51 | public function futureTick(callable $callback);
52 |
53 | /**
54 | * Schedules a callback to be executed immediately in the next tick.
55 | *
56 | * This function should be used when a callback needs to be executed later,
57 | * but needs to do so before any more event callbacks are invoked.
58 | *
59 | * @param callable $callback
60 | */
61 | public function nextTick(callable $callback);
62 |
63 | /**
64 | * Checks if the event loop is currently running.
65 | *
66 | * @return bool True if the event loop is running, otherwise false.
67 | */
68 | public function isRunning();
69 |
70 | /**
71 | * Executes a single iteration of the event loop.
72 | */
73 | public function tick();
74 |
75 | /**
76 | * Runs the event loop until there are no more events to process.
77 | */
78 | public function run();
79 |
80 | /**
81 | * Stops the event loop execution.
82 | */
83 | public function stop();
84 | }
85 |
--------------------------------------------------------------------------------
/src/Timers/TimerDevice.php:
--------------------------------------------------------------------------------
1 |
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not
6 | * use this file except in compliance with the License. You may obtain a copy
7 | * of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 | * License for the specific language governing permissions and limitations
15 | * under the License.
16 | */
17 |
18 | namespace Evflow\Timers;
19 |
20 | use Evflow\EventDeviceInterface;
21 | use Evflow\LoopInterface;
22 |
23 | /**
24 | * Event device for scheduling timers to be triggered at certain time intervals.
25 | */
26 | class TimerDevice implements EventDeviceInterface
27 | {
28 | const MICROSECONDS_PER_SECOND = 1000000;
29 |
30 | private $timers;
31 | private $timerQueue;
32 | private $currentTime;
33 |
34 | /**
35 | * Creates a new timer event device instance.
36 | */
37 | public function __construct()
38 | {
39 | $this->timers = new \SplObjectStorage();
40 | $this->timerQueue = new \SplPriorityQueue();
41 | }
42 |
43 | public function addTimer(Timer $timer)
44 | {
45 | $this->updateTime();
46 | $callbackTime = $this->currentTime + $timer->getInterval();
47 | $this->timers->attach($timer, $callbackTime);
48 | $this->timerQueue->insert($timer, -$callbackTime);
49 | }
50 |
51 | public function updateTime()
52 | {
53 | $this->currentTime = microtime(true) * self::MICROSECONDS_PER_SECOND;
54 | }
55 |
56 | /**
57 | * Polls the event device to process new incoming events.
58 | *
59 | * @see http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#The_special_problem_of_being_too_ear
60 | */
61 | public function poll(LoopInterface $loop, $timeout)
62 | {
63 | // update internal clock
64 | $this->updateTime();
65 |
66 | if ($timeout !== 0) {
67 | $nextTimer = $this->timerQueue->top();
68 | $timeUntilNextTimer = $this->timers[$nextTimer] - $this->currentTime + 1;
69 | $sleepAmount = max(0, $timeUntilNextTimer);
70 |
71 | if ($timeout > 0) {
72 | $sleepAmount = min($timeout, $sleepAmount);
73 | }
74 |
75 | usleep($sleepAmount);
76 | }
77 |
78 | // check for ready timers
79 | while (!$this->timerQueue->isEmpty()) {
80 | $timer = $this->timerQueue->top();
81 |
82 | // if the target time has passed, call the callback
83 | if ($this->currentTime > $this->timers[$timer]) {
84 | // add callback to future tick queue
85 | $loop->futureTick(function () use ($timer) {
86 | $callback = $timer->getCallback();
87 | $callback();
88 | });
89 |
90 | // remove timer from device
91 | //if (!$timer->isPeriodic()) {
92 | $this->timers->detach($timer);
93 | $this->timerQueue->extract();
94 | //}
95 | } else {
96 | break;
97 | }
98 | }
99 | }
100 |
101 | /**
102 | * @inheritDoc
103 | */
104 | public function isActive()
105 | {
106 | return $this->timers->count() > 0;
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/src/EventDeviceBinder.php:
--------------------------------------------------------------------------------
1 |
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not
6 | * use this file except in compliance with the License. You may obtain a copy
7 | * of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 | * License for the specific language governing permissions and limitations
15 | * under the License.
16 | */
17 |
18 | namespace Evflow;
19 |
20 | /**
21 | * Manages a group of event devices on behalf of an event loop.
22 | */
23 | class EventDeviceBinder implements \IteratorAggregate
24 | {
25 | /**
26 | * @var array A map of device types to the instances bound to them.
27 | */
28 | protected $deviceBindings = [];
29 |
30 | /**
31 | * Gets the number of active event devices.
32 | *
33 | * @return int
34 | */
35 | public function activeDeviceCount()
36 | {
37 | $count = 0;
38 | foreach ($this->deviceBindings as $device) {
39 | if ($device->isActive()) {
40 | $count++;
41 | }
42 | }
43 |
44 | return $count;
45 | }
46 |
47 | /**
48 | * Binds an event device instance to a type, or to itself if no
49 | * interface is specified.
50 | *
51 | * @param EventDeviceInterface $device The object instance to bind.
52 | * @param string $type The type name to bind to.
53 | */
54 | public function bindDevice(EventDeviceInterface $instance, $type = null)
55 | {
56 | if ($type === null) {
57 | $type = get_class($instance);
58 | } else {
59 | // check if the given instance implements the interface being bound to
60 | if (!($instance instanceof $type)) {
61 | throw new TypeException('Given instance does not implement the class or interface "'.$type.'".');
62 | }
63 |
64 | // check if the type or class exists
65 | if (!class_exists($type) && !interface_exists($type)) {
66 | throw new TypeException('The class or interface "'.$type.'" does not exist.');
67 | }
68 | }
69 |
70 | $this->deviceBindings[$type] = $instance;
71 | }
72 |
73 | /**
74 | * Gets an attached event device instance of a given type, or creates a new
75 | * instance of one cannot be found.
76 | *
77 | * @param string $type The type of the event device.
78 | * @param string $defaultType The type to use if an instance of the given type cannot be found.
79 | *
80 | * @return EventDeviceInterface An event device instance.
81 | *
82 | * @throws TypeException Thrown if a new instance of a type could not be created.
83 | */
84 | public function fetchDevice($type)
85 | {
86 | if (isset($this->deviceBindings[$type])) {
87 | return $this->deviceBindings[$type];
88 | }
89 |
90 | throw new \Exception('No instance for "'.$type.'" bound.');
91 | }
92 |
93 | /**
94 | * Unbinds a device instance from a type if bound.
95 | *
96 | * @param string $type The type name to unbind.
97 | */
98 | public function unbindDevice($type)
99 | {
100 | // check if the device exists
101 | if (isset($this->deviceBindings[$type])) {
102 | unset($this->deviceBindings[$type]);
103 | }
104 | }
105 |
106 | /**
107 | * Gets an iterator for looping over each event device.
108 | *
109 | * @return \Iterator
110 | */
111 | public function getIterator()
112 | {
113 | return new \ArrayIterator($this->deviceBindings);
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/src/StreamEventDevice.php:
--------------------------------------------------------------------------------
1 |
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not
6 | * use this file except in compliance with the License. You may obtain a copy
7 | * of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 | * License for the specific language governing permissions and limitations
15 | * under the License.
16 | */
17 |
18 | namespace Evflow;
19 |
20 | /**
21 | * An event device that watches streams for read and write activity.
22 | */
23 | class StreamEventDevice implements EventDeviceInterface
24 | {
25 | const READ = 1;
26 | const WRITE = 2;
27 |
28 | /**
29 | * @var array An array of streams to poll for reading.
30 | */
31 | protected $readStreams = [];
32 |
33 | /**
34 | * @var array An array of streams to poll for writing.
35 | */
36 | protected $writeStreams = [];
37 |
38 | /**
39 | * @var array A map of streams to callbacks to invoke when an event occurs.
40 | */
41 | protected $callbacks = [];
42 |
43 | /**
44 | * Registers interest in status changes on a stream.
45 | *
46 | * @param resource $stream A stream resource to watch for status changes.
47 | * @param int $mode The stream modes to watch for.
48 | * @param callable $callback A callback to invoke when an event on the stream occurs.
49 | */
50 | public function addStream($stream, $mode, $callback)
51 | {
52 | if (($mode & self::READ) === self::READ) {
53 | $this->readStreams[(int)$stream] = $stream;
54 | }
55 |
56 | if (($mode & self::WRITE) === self::WRITE) {
57 | $this->writeStreams[(int)$stream] = $stream;
58 | }
59 |
60 | $this->callbacks[(int)$stream] = $callback;
61 | }
62 |
63 | /**
64 | * Removes a stream from the device, stopping any listening.
65 | *
66 | * @param resource $stream The stream to remove.
67 | * @param int $mode The modes to stop watching for.
68 | */
69 | public function removeStream($stream, $mode = 3)
70 | {
71 | if (($mode & self::READ) === self::READ && isset($this->readStreams[(int)$stream])) {
72 | unset($this->readStreams[(int)$stream]);
73 | }
74 |
75 | if (($mode & self::WRITE) === self::WRITE && isset($this->writeStreams[(int)$stream])) {
76 | unset($this->writeStreams[(int)$stream]);
77 | }
78 |
79 | if (isset($this->callbacks[(int)$stream])) {
80 | unset($this->callbacks[(int)$stream]);
81 | }
82 | }
83 |
84 | /**
85 | * @inheritDoc
86 | */
87 | public function poll(LoopInterface $loop, $timeout)
88 | {
89 | $read = array_values($this->readStreams);
90 | $write = array_values($this->writeStreams);
91 | $except = [];
92 |
93 | // calculate timeout values
94 | $tv_sec = $timeout === -1 ? null : 0;
95 | $tv_usec = $timeout === -1 ? null : $timeout;
96 |
97 | if (stream_select($read, $write, $except, $tv_sec, $tv_usec) !== false) {
98 | foreach (array_merge($read, $write) as $stream) {
99 | $loop->futureTick(function () use ($stream) {
100 | $this->callbacks[(int)$stream]($stream);
101 | if (!is_resource($stream)) {
102 | $this->removeStream($stream);
103 | }
104 | });
105 | }
106 | }
107 | }
108 |
109 | /**
110 | * @inheritDoc
111 | */
112 | public function isActive()
113 | {
114 | return count($this->readStreams) + count($this->writeStreams) > 0;
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/src/Loop.php:
--------------------------------------------------------------------------------
1 |
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not
6 | * use this file except in compliance with the License. You may obtain a copy
7 | * of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 | * License for the specific language governing permissions and limitations
15 | * under the License.
16 | */
17 |
18 | namespace Evflow;
19 |
20 | /**
21 | * Facade for accessing the global event loop.
22 | */
23 | final class Loop
24 | {
25 | /**
26 | * The event loop instance being used as the default loop.
27 | *
28 | * @var LoopInterface
29 | */
30 | private static $loopInstance;
31 |
32 | /**
33 | * Indicates if the event loop should automatically begin running at the
34 | * end of the current process' main code execution.
35 | *
36 | * @var bool
37 | */
38 | private static $autoStart = true;
39 |
40 | /**
41 | * Initializes the global event loop.
42 | *
43 | * By default the event loop will register itself to run just before the
44 | * program exits. If this method is never called (and thus the loop never
45 | * used), then the loop will never get registered to run and won't disturb
46 | * normal execution flow at all.
47 | *
48 | * @param LoopInterface $loop The loop instance to use as the global event loop.
49 | */
50 | public static function init(LoopInterface $loop = null)
51 | {
52 | // check if the loop was already initialized
53 | if (self::$loopInstance instanceof LoopInterface) {
54 | throw new LoopInitializedException('Loop already initialized.');
55 | }
56 |
57 | // use the given instance, or create a new one if none given
58 | self::$loopInstance = !!$loop ? $loop : new NativeLoop();
59 |
60 | // run the global event loop just before the program exits
61 | register_shutdown_function(function () {
62 | if (self::$autoStart) {
63 | self::run();
64 | }
65 | });
66 | }
67 |
68 | /**
69 | * Gets the default event loop instance being used.
70 | *
71 | * If the event loop has not been initialized it will be initialized with
72 | * default values.
73 | *
74 | * @return LoopInterface
75 | */
76 | public static function instance()
77 | {
78 | if (!self::$loopInstance) {
79 | self::init();
80 | }
81 |
82 | return self::$loopInstance;
83 | }
84 |
85 | /**
86 | * Enables the automatic execution of the event loop at the end of the current thread.
87 | */
88 | public static function enableAutoStart()
89 | {
90 | self::$autoStart = true;
91 | }
92 |
93 | /**
94 | * Disables the automatic execution of the event loop at the end of the current thread.
95 | */
96 | public static function disableAutoStart()
97 | {
98 | self::$autoStart = false;
99 | }
100 |
101 | /**
102 | * Checks if the event loop is currently running.
103 | *
104 | * @return bool True if the event loop is running, otherwise false.
105 | */
106 | public static function isRunning()
107 | {
108 | return self::instance()->isRunning();
109 | }
110 |
111 | /**
112 | * Executes a single iteration of the event loop.
113 | */
114 | public static function tick()
115 | {
116 | self::instance()->tick();
117 | }
118 |
119 | /**
120 | * Runs all tasks in the global event loop.
121 | */
122 | public static function run()
123 | {
124 | self::instance()->run();
125 | }
126 |
127 | /**
128 | * Stops the event loop execution.
129 | */
130 | public static function stop()
131 | {
132 | self::instance()->stop();
133 | }
134 |
135 | // prevents instantiation
136 | private function __construct()
137 | {
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/src/NativeLoop.php:
--------------------------------------------------------------------------------
1 |
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not
6 | * use this file except in compliance with the License. You may obtain a copy
7 | * of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 | * License for the specific language governing permissions and limitations
15 | * under the License.
16 | */
17 |
18 | namespace Evflow;
19 |
20 | use Psr\Log\LoggerAwareInterface;
21 | use Psr\Log\LoggerInterface;
22 | use Psr\Log\LogLevel;
23 |
24 | /**
25 | * The default event loop implementation that has no external dependencies.
26 | */
27 | class NativeLoop implements LoopInterface, LoggerAwareInterface
28 | {
29 | /**
30 | * @var EventDeviceBinder A collection of event devices attached to this event loop.
31 | */
32 | protected $devices;
33 |
34 | /**
35 | * @var \SplQueue A queue of callbacks to be invoked in the next tick.
36 | */
37 | private $nextTickQueue;
38 |
39 | /**
40 | * @var \SplQueue A queue of callbacks to be invoked in a future tick.
41 | */
42 | private $futureTickQueue;
43 |
44 | /**
45 | * @var int The current running tick count.
46 | */
47 | private $tickCount = 0;
48 |
49 | /**
50 | * @var int The minimum allowed time between ticks in microseconds.
51 | */
52 | private $minPollInterval = 1000;
53 |
54 | /**
55 | * @var LoggerInterface A logger for sending log information to.
56 | */
57 | private $logger;
58 |
59 | /**
60 | * @var bool Flag that indicates if the event loop is currently running.
61 | */
62 | private $running = false;
63 |
64 | /**
65 | * @var bool A flag indicating if the event loop is in the idle state.
66 | */
67 | private $idle = false;
68 |
69 | /**
70 | * Creates a new event loop instance.
71 | *
72 | * The created event loop operates completely independently from the global
73 | * event loop and other event loop instances.
74 | */
75 | public function __construct()
76 | {
77 | $this->nextTickQueue = new \SplQueue();
78 | $this->futureTickQueue = new \SplQueue();
79 | $this->devices = new EventDeviceBinder();
80 | }
81 |
82 | /**
83 | * {@inheritDoc}
84 | */
85 | public function bindDevice(EventDeviceInterface $instance, $type = null)
86 | {
87 | $this->devices->bindDevice($instance, $type);
88 | $this->log(LogLevel::DEBUG, 'New event device of type '.get_class($instance).' bound.');
89 | }
90 |
91 | public function fetchDevice($type)
92 | {
93 | return $this->devices->fetchDevice($type);
94 | }
95 |
96 | /**
97 | * Checks if the event loop is in the idle state.
98 | *
99 | * The event loop enters the idle state when there are no pending or future
100 | * callback functions to invoke. There may still be active event devices
101 | * attached to the loop while it is idle, and will not exit until all event
102 | * devices are inactive.
103 | *
104 | * @return bool
105 | */
106 | public function isIdle()
107 | {
108 | return $this->idle;
109 | }
110 |
111 | /**
112 | * {@inheritDoc}
113 | */
114 | public function isRunning()
115 | {
116 | return $this->running;
117 | }
118 |
119 | /**
120 | * Gets the current tick count of the event loop.
121 | *
122 | * The tick count is an integer that indicates how many iterations the event
123 | * loop has made since it started. The first tick begins at tick 0. The tick
124 | * count is not reset if the event loop is stopped and resumed multiple
125 | * times.
126 | *
127 | * @return int
128 | */
129 | public function getTickCount()
130 | {
131 | return $this->tickCount;
132 | }
133 |
134 | /**
135 | * Sets a logger instance to send event log info to.
136 | *
137 | * @param LoggerInterface $logger
138 | */
139 | public function setLogger(LoggerInterface $logger)
140 | {
141 | $this->logger = $logger;
142 | $this->log(LogLevel::DEBUG, 'New logger registered. Hello logger!');
143 | }
144 |
145 | /**
146 | * @inheritDoc
147 | */
148 | public function nextTick(callable $callback)
149 | {
150 | $this->nextTickQueue->enqueue($callback);
151 | $this->log(LogLevel::INFO, 'Enqueued new next tick callback.');
152 | }
153 |
154 | /**
155 | * @inheritDoc
156 | */
157 | public function futureTick(callable $callback)
158 | {
159 | $this->futureTickQueue->enqueue($callback);
160 | $this->log(LogLevel::INFO, 'Enqueued new future tick callback.');
161 | }
162 |
163 | /**
164 | * @inheritDoc
165 | */
166 | public function tick()
167 | {
168 | // invoke all callbacks scheduled for this tick
169 | while (!$this->nextTickQueue->isEmpty()) {
170 | $callback = $this->nextTickQueue->dequeue();
171 | $this->log(LogLevel::INFO, 'Invoking next scheduled tick callback.');
172 | $callback();
173 | }
174 |
175 | // invoke the next future tick callback
176 | if (!$this->futureTickQueue->isEmpty()) {
177 | $callback = $this->futureTickQueue->dequeue();
178 | $this->log(LogLevel::INFO, 'Invoking next future tick callback.');
179 | $callback();
180 | }
181 |
182 | // poll each device & return immediately
183 | $timeout = 0;
184 |
185 | // if the loop is idle and only one device is active we can poll it forever
186 | if ($this->devices->activeDeviceCount() === 1 && $this->idle) {
187 | $timeout = -1;
188 | $this->log(LogLevel::DEBUG, 'Only one active event device. Polling indefinitely.');
189 | }
190 |
191 | // poll all event devices for new events
192 | foreach ($this->devices as $device) {
193 | // only poll the device if it is active
194 | if ($device->isActive()) {
195 | $device->poll($this, $timeout);
196 | }
197 | }
198 |
199 | $this->tickCount++;
200 | }
201 |
202 | /**
203 | * @inheritDoc
204 | */
205 | public function run()
206 | {
207 | $this->running = true;
208 | $this->log(LogLevel::INFO, 'Event loop started.');
209 |
210 | // run the event loop until instructed otherwise
211 | while ($this->running) {
212 | // execute a single tick
213 | $this->tick();
214 |
215 | // update idle state
216 | if ($this->nextTickQueue->isEmpty() && $this->futureTickQueue->isEmpty()) {
217 | if (!$this->idle) {
218 | $this->idle = true;
219 | $this->log(LogLevel::INFO, 'Entered idle state.');
220 | }
221 | } elseif ($this->idle) {
222 | $this->idle = false;
223 | $this->log(LogLevel::INFO, 'Leaving idle state.');
224 | }
225 |
226 | // if we have no more work to do, stop wasting time
227 | if ($this->idle && $this->devices->activeDeviceCount() === 0) {
228 | $this->log(LogLevel::DEBUG, 'Nothing left to do.');
229 | $this->stop();
230 | }
231 |
232 | usleep($this->minPollInterval);
233 | }
234 |
235 | $this->log(LogLevel::INFO, 'Event loop stopped.');
236 | }
237 |
238 | /**
239 | * @inheritDoc
240 | */
241 | public function stop()
242 | {
243 | $this->log(LogLevel::DEBUG, 'Stopping event loop.');
244 | $this->running = false;
245 | }
246 |
247 | /**
248 | * Outputs a logging message to a configured logger, if any.
249 | *
250 | * @param mixed $level
251 | * @param string $message
252 | * @param array $context
253 | */
254 | protected function log($level, $message, array $context = array())
255 | {
256 | if ($this->logger instanceof LoggerInterface) {
257 | $context['tick'] = $this->getTickCount();
258 | $this->logger->log($level, $message, $context);
259 | }
260 | }
261 | }
262 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | # Apache License
2 | Version 2.0, January 2004
3 |
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | ## 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction, and
11 | distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by the copyright
14 | owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all other entities
17 | that control, are controlled by, or are under common control with that entity.
18 | For the purposes of this definition, "control" means (i) the power, direct or
19 | indirect, to cause the direction or management of such entity, whether by
20 | contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity exercising
24 | permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications, including
27 | but not limited to software source code, documentation source, and configuration
28 | files.
29 |
30 | "Object" form shall mean any form resulting from mechanical transformation or
31 | translation of a Source form, including but not limited to compiled object code,
32 | generated documentation, and conversions to other media types.
33 |
34 | "Work" shall mean the work of authorship, whether in Source or Object form, made
35 | available under the License, as indicated by a copyright notice that is included
36 | in or attached to the work (an example is provided in the Appendix below).
37 |
38 | "Derivative Works" shall mean any work, whether in Source or Object form, that
39 | is based on (or derived from) the Work and for which the editorial revisions,
40 | annotations, elaborations, or other modifications represent, as a whole, an
41 | original work of authorship. For the purposes of this License, Derivative Works
42 | shall not include works that remain separable from, or merely link (or bind by
43 | name) to the interfaces of, the Work and Derivative Works thereof.
44 |
45 | "Contribution" shall mean any work of authorship, including the original version
46 | of the Work and any modifications or additions to that Work or Derivative Works
47 | thereof, that is intentionally submitted to Licensor for inclusion in the Work
48 | by the copyright owner or by an individual or Legal Entity authorized to submit
49 | on behalf of the copyright owner. For the purposes of this definition,
50 | "submitted" means any form of electronic, verbal, or written communication sent
51 | to the Licensor or its representatives, including but not limited to
52 | communication on electronic mailing lists, source code control systems, and
53 | issue tracking systems that are managed by, or on behalf of, the Licensor for
54 | the purpose of discussing and improving the Work, but excluding communication
55 | that is conspicuously marked or otherwise designated in writing by the copyright
56 | owner as "Not a Contribution."
57 |
58 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf
59 | of whom a Contribution has been received by Licensor and subsequently
60 | incorporated within the Work.
61 |
62 | ## 2. Grant of Copyright License.
63 |
64 | Subject to the terms and conditions of this License, each Contributor hereby
65 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
66 | irrevocable copyright license to reproduce, prepare Derivative Works of,
67 | publicly display, publicly perform, sublicense, and distribute the Work and such
68 | Derivative Works in Source or Object form.
69 |
70 | ## 3. Grant of Patent License.
71 |
72 | Subject to the terms and conditions of this License, each Contributor hereby
73 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
74 | irrevocable (except as stated in this section) patent license to make, have
75 | made, use, offer to sell, sell, import, and otherwise transfer the Work, where
76 | such license applies only to those patent claims licensable by such Contributor
77 | that are necessarily infringed by their Contribution(s) alone or by combination
78 | of their Contribution(s) with the Work to which such Contribution(s) was
79 | submitted. If You institute patent litigation against any entity (including a
80 | cross-claim or counterclaim in a lawsuit) alleging that the Work or a
81 | Contribution incorporated within the Work constitutes direct or contributory
82 | patent infringement, then any patent licenses granted to You under this License
83 | for that Work shall terminate as of the date such litigation is filed.
84 |
85 | ## 4. Redistribution.
86 |
87 | You may reproduce and distribute copies of the Work or Derivative Works thereof
88 | in any medium, with or without modifications, and in Source or Object form,
89 | provided that You meet the following conditions:
90 |
91 | 1. You must give any other recipients of the Work or Derivative Works a copy of
92 | this License; and
93 |
94 | 2. You must cause any modified files to carry prominent notices stating that
95 | You changed the files; and
96 |
97 | 3. You must retain, in the Source form of any Derivative Works that You
98 | distribute, all copyright, patent, trademark, and attribution notices from
99 | the Source form of the Work, excluding those notices that do not pertain to
100 | any part of the Derivative Works; and
101 |
102 | 4. If the Work includes a "NOTICE" text file as part of its distribution, then
103 | any Derivative Works that You distribute must include a readable copy of the
104 | attribution notices contained within such NOTICE file, excluding those
105 | notices that do not pertain to any part of the Derivative Works, in at least
106 | one of the following places: within a NOTICE text file distributed as part
107 | of the Derivative Works; within the Source form or documentation, if
108 | provided along with the Derivative Works; or, within a display generated by
109 | the Derivative Works, if and wherever such third-party notices normally
110 | appear. The contents of the NOTICE file are for informational purposes only
111 | and do not modify the License. You may add Your own attribution notices
112 | within Derivative Works that You distribute, alongside or as an addendum to
113 | the NOTICE text from the Work, provided that such additional attribution
114 | notices cannot be construed as modifying the License.
115 |
116 | You may add Your own copyright statement to Your modifications and may provide
117 | additional or different license terms and conditions for use, reproduction, or
118 | distribution of Your modifications, or for any such Derivative Works as a whole,
119 | provided Your use, reproduction, and distribution of the Work otherwise complies
120 | with the conditions stated in this License.
121 |
122 | ## 5. Submission of Contributions.
123 |
124 | Unless You explicitly state otherwise, any Contribution intentionally submitted
125 | for inclusion in the Work by You to the Licensor shall be under the terms and
126 | conditions of this License, without any additional terms or conditions.
127 | Notwithstanding the above, nothing herein shall supersede or modify the terms of
128 | any separate license agreement you may have executed with Licensor regarding
129 | such Contributions.
130 |
131 | ## 6. Trademarks.
132 |
133 | This License does not grant permission to use the trade names, trademarks,
134 | service marks, or product names of the Licensor, except as required for
135 | reasonable and customary use in describing the origin of the Work and
136 | reproducing the content of the NOTICE file.
137 |
138 | ## 7. Disclaimer of Warranty.
139 |
140 | Unless required by applicable law or agreed to in writing, Licensor provides the
141 | Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
142 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
143 | including, without limitation, any warranties or conditions of TITLE,
144 | NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
145 | solely responsible for determining the appropriateness of using or
146 | redistributing the Work and assume any risks associated with Your exercise of
147 | permissions under this License.
148 |
149 | ## 8. Limitation of Liability.
150 |
151 | In no event and under no legal theory, whether in tort (including negligence),
152 | contract, or otherwise, unless required by applicable law (such as deliberate
153 | and grossly negligent acts) or agreed to in writing, shall any Contributor be
154 | liable to You for damages, including any direct, indirect, special, incidental,
155 | or consequential damages of any character arising as a result of this License or
156 | out of the use or inability to use the Work (including but not limited to
157 | damages for loss of goodwill, work stoppage, computer failure or malfunction, or
158 | any and all other commercial damages or losses), even if such Contributor has
159 | been advised of the possibility of such damages.
160 |
161 | ## 9. Accepting Warranty or Additional Liability.
162 |
163 | While redistributing the Work or Derivative Works thereof, You may choose to
164 | offer, and charge a fee for, acceptance of support, warranty, indemnity, or
165 | other liability obligations and/or rights consistent with this License. However,
166 | in accepting such obligations, You may act only on Your own behalf and on Your
167 | sole responsibility, not on behalf of any other Contributor, and only if You
168 | agree to indemnify, defend, and hold each Contributor harmless for any liability
169 | incurred by, or claims asserted against, such Contributor by reason of your
170 | accepting any such warranty or additional liability.
171 |
172 | END OF TERMS AND CONDITIONS
173 |
174 | ## APPENDIX: How to apply the Apache License to your work.
175 |
176 | To apply the Apache License to your work, attach the following boilerplate
177 | notice, with the fields enclosed by brackets "{}" replaced with your own
178 | identifying information. (Don't include the brackets!) The text should be
179 | enclosed in the appropriate comment syntax for the file format. We also
180 | recommend that a file or class name and description of purpose be included on
181 | the same "printed page" as the copyright notice for easier identification within
182 | third-party archives.
183 |
184 | Copyright {yyyy} {name of copyright owner}
185 |
186 | Licensed under the Apache License, Version 2.0 (the "License"); you may not
187 | use this file except in compliance with the License. You may obtain a copy
188 | of the License at
189 |
190 | http://www.apache.org/licenses/LICENSE-2.0
191 |
192 | Unless required by applicable law or agreed to in writing, software
193 | distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
194 | WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
195 | License for the specific language governing permissions and limitations
196 | under the License.
197 |
--------------------------------------------------------------------------------
/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
158 |
--------------------------------------------------------------------------------