Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
636 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
name: Documentation | ||
|
||
on: | ||
push: | ||
branches: [master] | ||
|
||
jobs: | ||
publish: | ||
runs-on: ubuntu-latest | ||
name: 'Publish documentation' | ||
steps: | ||
- name: Checkout | ||
uses: actions/checkout@v2 | ||
- name: Setup PHP | ||
uses: shivammathur/setup-php@v2 | ||
with: | ||
php-version: '7.4' | ||
- name: Install halsey/journal | ||
run: composer global require halsey/journal | ||
- name: Generate | ||
run: composer global exec 'journal generate' | ||
- name: Push | ||
uses: peaceiris/actions-gh-pages@v3 | ||
with: | ||
github_token: ${{ secrets.GITHUB_TOKEN }} | ||
publish_dir: ./.tmp_journal/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
<?php | ||
declare(strict_types = 1); | ||
|
||
use Halsey\Journal\{ | ||
Config, | ||
Menu\Entry, | ||
}; | ||
use Innmind\Url\Path; | ||
|
||
return static function(Config $config): Config { | ||
return $config | ||
->package('innmind', 'operating-system', null, 'OperatingSystem') | ||
->menu( | ||
Entry::markdown( | ||
'Getting started', | ||
Path::of('readme.md'), | ||
), | ||
Entry::section( | ||
'Use cases', | ||
Entry::markdown( | ||
'Manipulating time', | ||
Path::of('use_cases/time.md'), | ||
), | ||
Entry::markdown( | ||
'Filesystem', | ||
Path::of('use_cases/filesystem.md'), | ||
), | ||
Entry::markdown( | ||
'HTTP Client', | ||
Path::of('use_cases/http.md'), | ||
), | ||
Entry::markdown( | ||
'Processes', | ||
Path::of('use_cases/processes.md'), | ||
), | ||
Entry::markdown( | ||
'Inter Process Communication', | ||
Path::of('use_cases/ipc.md'), | ||
), | ||
Entry::markdown( | ||
'Socket communication', | ||
Path::of('use_cases/socket.md'), | ||
), | ||
Entry::markdown( | ||
'Handling process signals', | ||
Path::of('use_cases/signals.md'), | ||
), | ||
)->alwaysOpen(), | ||
Entry::section( | ||
'Advanced usage', | ||
Entry::markdown( | ||
'Logging all operations', | ||
Path::of('advanced/logging.md'), | ||
), | ||
Entry::markdown( | ||
'Extensions', | ||
Path::of('advanced/extensions.md'), | ||
), | ||
), | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
# Extensions | ||
|
||
The advantage of having all operating system's operations done through a single abstraction is that you can easily add behaviour on top of that. | ||
|
||
## Debugger | ||
|
||
The [`innmind/debug` library](https://github.com/innmind/debug) provides a decorator that will send all the operations of the operating system to your [profiler (`innmind/profiler`)](https://github.com/innmind/profiler). | ||
|
||
This debugger works for both http requests and cli applications. | ||
|
||
**Note**: you can either add this debugger yourself or you can use [`innmind/http-framework`](https://github.com/innmind/httpframework) and [`innmind/cli-framework`](https://github.com/innmind/cliframework) that will automatically enable this debugger when there is a dsn provided in the `PROFILER` environment variable. | ||
|
||
## Silent Cartographer | ||
|
||
The [`innmind/silent-cartographer`](https://github.com/Innmind/SilentCartographer) is a CLI tool that will display all operating system's operations from all the PHP processes using this extension. This is useful when you want a glance at what's going on on your machine without the need to go through all the log files (if there's any). | ||
|
||
**Note**: if you use [`innmind/http-framework`](https://github.com/innmind/httpframework) and [`innmind/cli-framework`](https://github.com/innmind/cliframework) this extension is automatically enabled. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
# Logging all operations | ||
|
||
If you want to trace everything that is done on your operating system you can use the logger decorator that will automatically write to your log file (almost) all operations. | ||
|
||
**Note**: data and actions done on a socket are not logged as well as processes output to prevent logging too many data (at least for now). | ||
|
||
```php | ||
use Innmind\OperatingSystem\OperatingSystem\Logger; | ||
use Psr\Log\LoggerInterface; | ||
|
||
$os = new Logger( | ||
$os, | ||
/* any instance of LoggerInterface */ | ||
); | ||
``` | ||
|
||
Now operations done with the new `$os` object will be written to your logs. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
# Getting started | ||
|
||
This library is here to help abstract all the operations that involve the operating system the PHP code run on. | ||
|
||
This kind of abstraction is useful to move away from implementation details and towards user intentions. By abstracting implementations details it becomes easier to port an application into a new environment and accomodate the differences at the implementation level without any change in the user intentions. | ||
|
||
The other advantage to use higher level abstractions is to enable end user to build more complex applications by freeing them from thinking of low level details. | ||
|
||
For concrete examples have a look at the use cases available in the sidebar. | ||
|
||
**Note**: this library is a small overlay on top of a set of individual libraries that contain the concrete abstractions. So you can start using only a subset of abstractions in your code as a starting point. | ||
|
||
## Installation | ||
|
||
```sh | ||
composer require innmind/operating-system | ||
``` | ||
|
||
## Basic usage | ||
|
||
```php | ||
use Innmind\OperatingSystem\Factory; | ||
|
||
$os = Factory::build(); | ||
``` | ||
|
||
There's nothing more to add to start using this abstraction. Head toward the use cases to understand all the things you can do with it. | ||
|
||
**Note**: This library doesn't work on windows environments. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
# Filesystem | ||
|
||
Like [time](time.md) the filesystem can be easily accessed with PHP builtin functions but is a source of implicits and state. To move away from these problems the filesystem here is considered as an immutable structure that you _mount_ in your code. This forces you explicit what directories you are accessing and always verify that the structure you want to manipulate is as you expect. | ||
|
||
## Examples | ||
|
||
### Doing a filesystem backup | ||
|
||
```php | ||
use Innmind\Filesystem\Adapter; | ||
use Innmind\Url\Path; | ||
|
||
$backup = function(Adapter $source, Adapter $target): void { | ||
$source->all()->foreach(function($file) use ($target): void { | ||
// $file can be either a concrete file or directory | ||
$target->add($file); | ||
}); | ||
}; | ||
$backup( | ||
$os->filesystem()->mount(Path::of('/path/to/source/')), | ||
$os->filesystem()->mount(Path::of('/path/to/target/')), | ||
); | ||
``` | ||
|
||
Here we copy all the files from a local directory to another, but the `backup` function isn't aware of the locality of filesystems meaning that the source or target could be swapped to use remote filesystem (such as [S3](https://github.com/innmind/s3)). | ||
|
||
### Adding a file in a subdirectory | ||
|
||
```php | ||
use Innmind\Filesystem\{ | ||
Adapter, | ||
File\File, | ||
Directory\Directory, | ||
Name, | ||
}; | ||
use Innmind\Url\Path; | ||
use Innmind\Stream\Readable\Stream; | ||
|
||
$addUserPicture = function(Adapter $filesystem, string $userId, File $picture): void { | ||
if ($filesystem->contains(new Name($userId))) { | ||
$userDirectory = $filesystem->get(new Name($userId)); | ||
} else { | ||
$userDirectory = Directory::named($userId); | ||
} | ||
|
||
$filesystem->add($userDirectory->add($picture)); | ||
}; | ||
$addUserPicture( | ||
$os->filesystem()->mount(Path::of('/path/to/users/data/')), | ||
'some-unique-id', | ||
File::named( | ||
'picture.png', | ||
Stream::open( | ||
Path::of($_FILES['some_file']['tmp_name']), | ||
), | ||
), | ||
); | ||
``` | ||
|
||
Here you are forced to explicit the creation of the user directory instead of implicitly assuming it has been created by a previous process. | ||
|
||
Once again the function is unaware where the file comes from or where it will be stored and simply how the process of adding the picture works. | ||
|
||
### Checking if a file exists | ||
|
||
Sometimes you don't want to mount a filesystem in order to know if a file or directory exist as you only want to check for their existence. | ||
|
||
Example of checking if a `maintenance.lock` exist to prevent your webapp from running: | ||
|
||
```php | ||
use Innmind\Url\Path; | ||
|
||
if ($os->filesystem()->contains(Path::of('/path/to/project/maintenance.lock'))) { | ||
throw new \RuntimeException('Application still in maintenance'); | ||
} | ||
|
||
// run normal webapp here | ||
``` | ||
|
||
Or you could check the existence of a directory that is required for another sub process to run correctly: | ||
|
||
```php | ||
use Innmind\Url\Path; | ||
|
||
if (!$os->filesystem()->contains(Path::of('/path/to/some/required/folder/'))) { | ||
$os->control()->processes()->execute($mkdirCommand); | ||
} | ||
|
||
$os->control()->processes()->execute($subProcessCommand); | ||
``` | ||
|
||
See [processes](processes.md) section on how to execute commands on your operating system. | ||
|
||
### Mounting the `tmp` folder | ||
|
||
Sometimes you want to use the `tmp` folder to write down files such as cache that can be saefly lost in case of a system reboot. You can easily mount this folder as any other folder like so: | ||
|
||
```php | ||
use Innmind\Filesystem\Adapter; | ||
|
||
$tmp = $os->filesystem()->mount($os->status()->tmp()); | ||
$tmp instanceof Adapter; // true | ||
``` | ||
|
||
It is a great way to forget about where the tmp folder is located and simply focus on what you need to do. And since you can use it as any other mounted filesystem you can change it for tests purposes. | ||
|
||
### Watching for changes on the filesystem | ||
|
||
A pattern we don't see much in PHP is an infinite loop to react to an event to perform another task. Here can build such pattern by watching for changes in a file or a directory. | ||
|
||
```php | ||
$runTests = $os->filesystem()->watch(Path::of('/path/to/project/src/')); | ||
|
||
$runTests(function() use ($os): void { | ||
$os->control()->processes()->execute($phpunitCommand); | ||
}); | ||
``` | ||
|
||
Here it will run phpunit tests every time the `src/` folder changes. Concrete examples of this pattern can be found in [`innmind/lab-station`](https://github.com/Innmind/LabStation/blob/develop/src/Agent/WatchSources.php#L38) to run a suite of tools when sources change or in [`halsey/journal`](https://github.com/halsey-php/journal/blob/develop/src/Command/Preview.php#L58) to rebuild the website when the markdown files change. | ||
|
||
**Important**: since there is not builtin way to watch for changes in a directory it checks the directory every second, so use it with care. Watching an individual file is a bit safer as it uses the `tail` command so there is no `sleep()` used. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
# HTTP Client | ||
|
||
## Calling an HTTP server | ||
|
||
```php | ||
use Innmind\Http\{ | ||
Message\Request\Request, | ||
Message\Method, | ||
Message\Response, | ||
ProtocolVersion, | ||
}; | ||
use Innmind\Url\Url; | ||
|
||
$http = $os->remote()->http(); | ||
|
||
$response = $http(new Request( | ||
Url::of('https://github.com'), | ||
Method::get(), | ||
new ProtocolVersion(2, 0), | ||
)); | ||
$response instanceof Response; // true | ||
``` | ||
|
||
All elements of a request/response call is built using objects to enforce correctness of the formatted messages. | ||
|
||
**Note**: since request and responses messages can be viewed either from a client or a server the model is abstracted in the standalone [`innmind/http` library](https://github.com/innmind/http). | ||
|
||
## Resiliency in a distributed system | ||
|
||
One of the first things taught when working with distributed systems is that they will intermittently fail. To prevent your app to crash for an occasional failure a common pattern is the _retry pattern_ with a backoff strategy allowing the client to retry safe requests a certain amount of time before giving up. You can use this pattern like so: | ||
|
||
```php | ||
use Innmind\OperatingSystem\OperatingSystem\Resilient; | ||
use Innmind\HttpTransport\ExponentialBackoffTransport; | ||
|
||
$os = new Resilient($os); | ||
$http = $os->remote()->http(); | ||
$http instanceof ExponentialBackoffTransport; // true | ||
``` | ||
|
||
Another strategy you can add on top of that is the [circuit breaker pattern](https://en.wikipedia.org/wiki/Circuit_breaker_design_pattern) that will stop sending request to a server known to have failed. | ||
|
||
```php | ||
use Innmind\HttpTransport\CircuitBreakerTransport; | ||
use Innmind\TimeContinuum\Earth\Period\Minute; | ||
|
||
$http = new CircuitBreakerTransport( | ||
$http, | ||
$os->clock(), | ||
new Minute(1), | ||
); | ||
$request = new Request(/* ...args */) | ||
$response = $http($request); | ||
// if the previous call failed then the next call wil not even be sent to the | ||
// server and the client will respond immediately unless 1 minute has elapsed | ||
// between the 2 calls | ||
$response = $http($request); | ||
``` | ||
|
||
**Note**: the circuit breaker works on host per host basis meaning if `server1.com` fails then calls to `server2.com` will still be sent. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
# Inter Process Communication (IPC) | ||
|
||
To communicate between processes on a same system there is 2 approaches: sharing memory or passing messages through a socket. | ||
|
||
The later is the safest of the two (but not exempt of problems) and you will find here the building blocks to communicate via a socket. | ||
|
||
**Note**: the adage `share state through messages and not messages through state` is a pillar of the [actor model](https://en.wikipedia.org/wiki/Actor_model) and [initially of object oriented programming](https://www.youtube.com/watch?v=7erJ1DV_Tlo). | ||
|
||
```php | ||
# process acting a server | ||
use Innmind\Socket\Address\Unix as Address; | ||
use Innmind\TimeContinuum\Earth\ElapsedPeriod; | ||
use Innmind\Immutable\Str; | ||
|
||
$server = $os->sockets()->open(Address::of('/tmp/foo')); | ||
$watch = $os->sockets()->watch(new ElapsedPeriod(1000))->forRead($server); | ||
|
||
while (true) { | ||
$ready = $watch(); | ||
|
||
if ($ready->toRead()->contains($server)) { | ||
$client = $server->accept(); | ||
$client->write(Str::of('Hello 👋')); | ||
$client->close(); | ||
} | ||
} | ||
``` | ||
|
||
```php | ||
# process acting as client | ||
use Innmind\Socket\Address\Unix as Address; | ||
use Innmind\TimeContinuum\Earth\ElapsedPeriod; | ||
|
||
$client = $os->sockets()->connectTo(Address::of('/tmp/foo')); | ||
$watch = $os->sockets()->watch(new ElapsedPeriod(1000))->forRead($client); | ||
|
||
do { | ||
$ready = $watch(); | ||
} while (!$ready->toRead()->contains($client)); | ||
|
||
echo $client->read()->toString(); | ||
``` | ||
|
||
In the case the server is started first then the client would print `Hello 👋`. | ||
|
||
**Important**: this is a very rough implementation of communication between processes. **DO NOT** use this simple implementation in your code, instead use a higher level API such as [`innmind/ipc`](https://github.com/innmind/ipc). |
Oops, something went wrong.