2. • Directeur technique chez Extension
• 14 ans d’expérience dans le web
• Créateur des usergroup
‘SymfonyMu’ et ‘DrupalMauritius’
• MSCC Member
• Twitter : @VanessaChellen
16. ReactPHP
Third-party libraries can use these components to create async network
clients/servers and more.
An event loop with low-level utilities
Streams abstraction async DNS resolver network client/server
HTTP client/server and
interaction with processes.
Low-level library for event-driven programming in PHP.
19. EventLoop
• Need to be in
same loop to be
interoperable
• Single event loop
• Factory pick the
best available loop
implementation
ReactEventLoopFactory::create()
20. Loop Implementations
StreamSelectLoop
¬ Only implementation that works out of the box with PHP5,3 – 7.x
¬ Default loop implementation
¬ Simple function call
¬ Limited to the max file descriptor FD_SETSIZE
- Not suitable when running thousands of concurrent threads
¬ < PHP7,3 use wall-clock time for future timers
- Not suitable for critical time sensitive applications if PHP < 7,3
ExtEventLoop
ExtEvLoop
ExtUvLoop
ExtLibeventLoop
ExtLibevLoop
22. Loop Interface
run()
¬ To run loop of course
¬ Keep running until no more tasks to perform
¬ Invoke only ONCE
¬ MUST NOT be called while the loop is running
stop()
¬ Explicitly stop a running loop
¬ Use with care
addTimer()
¬ Enqueue a callback
¬ Callback function
- MUST be able to accept a single parameter
- MAY use a function with no params
- MUST NOT return an exception
- WILL BE invoke only once
23. Loop Interface
addPeriodicTimer()
¬ Invoke callback repeatedly after given interval
¬ No particular timer resolution
¬ Work on a best effort basis
¬ Can be cancelled by cancelTimer()
futureTick()
¬ Used to schedule a callback
¬ Guaranteed to be executed in the order they are enqueued
¬ No way to cancel
addSignal(), removeSignal()
addReadStream(), addWriteStream(), removeReadStream(),
removeWriteStream()
26. Stream
Optimized for Async non-blocking I/O
Enable processing of huge data volume without storing
everything in memory at once
Abstraction to build higer-lever components & protocols
Streams can be
¬ Readable
¬ Writeable
¬ Duplex
27. ReadableStreamInterface
Interface for read-only streams & readable side of Duplex streams
Implements the EventEmitterInterface, allowing code to react to
certain events.
Event callback functions
¬ MUST be a valid callable that obeys strict parameter definitions
¬ MUST accept event parameters exactly as documented.
¬ MUST NOT throw an Exception
¬ MUST follow event semantics in order to be considered a well-behaving stream
data event, end event, error event, close event, isReadable(), pause(),
resume(), pipe(), close()
28. WritableStreamInterface
Interface for write-only streams and the writable side of duplex streams
Also implements EventEmitterInterface
Event callback functions
¬ MUST be a valid callable that obeys strict parameter definitions
¬ MUST accept event parameters exactly as documented.
¬ MUST NOT throw an Exception
¬ MUST follow event semantics in order to be considered a well-behaving stream
drain event, pipe event, error event, close event, isWritable(), write(),
end(), close()
29. DuplexStreamInterface
Interface for duplex streams (both readable and writable)
Built on top of the existing interfaces for readable and writable streams
Also implements EventEmitterInterface
Event callback functions
¬ MUST be a valid callable that obeys strict parameter definitions
¬ MUST accept event parameters exactly as documented.
¬ MUST NOT throw an Exception
¬ MUST follow event semantics in order to be considered a well-behaving stream
30. Creatings Streams
Rarely
needed/used
High level
consumers
usually deal with
stream usage
React/socket
Accept
plaintext TCP/IP
or TLS socket
connection
React/http
HTTP request
body streams
React/child-
process
Communication
with child
process via
process pipes
React/filesystem
(experimental)
Read / write
filesystem
36. Promise API
Deferred
Operation whose resolution is pending. It has separate promise and resolver parts.
¬ Promise(), resolve(), reject(), notify()
PromiseInterface
Common interface for all promise implementations.
¬ Promise
¬ FulfilledPromise
¬ RejectedPromise
¬ LazyPromise
39. Either return your promise, or call done() on it.
The intent of then() is to transform a promise's value and to pass or
return a new promise for the transformed value along to other parts of
your code.
The intent of done() is to consume a promise's value, transferring
responsibility for the value to your code.
43. Utility components
Cache
Async. Promise-
based cache
interface
ChildProcess
Event-driven
Library for
executing child
processes
PromiseTimer
Implementation
for timeouts
PromiseStream
Missing link
between
promise-land
and stream-land
#4:On the rise
Responsiveness
waste their time and to wait for a freezing application
Ne pas bloquer des actions
#5:“Oh, it’s cool! I can run my stuff in parallel!”. I may disappoint you but actually, it is not true, concurrency and parallelism is not the same thing.
When running something asynchronously it means a non-blocking execution without waiting for completion. Instead, parallelism means running multiple separate tasks at the same time as independent units of work.
Asynchronous code mostly deals with time (order of events).
#6:Sometimes you have to manage some complicated tasks, for example, a server for uploading videos. In this case, maybe responsiveness is not a key factor, but we come to resources waisting because an application has to wait. It can wait for file-system operations, for network communication, for database queries and so on. Often these I/O operations are extremely slow comparing to CPU calculations, for example, when we convert video files. And while we are slowly storing or reading a file, our CPU has to wait and does nothing, instead of doing some useful job. As we have already considered instead of waiting we can run these tasks in background.
#7:Of course, when PHP was created its goal was not to be a programming language that can be used to build large complicated applications. When it happened there was no JavaScript and no asynchronous stuff in mind. But now we have completely different PHP, which already has some native functionality for writing asynchronous code (like stream_select() function).
Yes, you can use Go or NodeJs to create asynchronous back-end applications, but it is not always the case. When you already have a solid background in PHP, it will be much easier for you just to dig in some libraries and tools for your use-case, instead of learning a new language and a new ecosystem. Such tools as ReactPHP or Amp allow you to write asynchronous code like you write it in NodeJS. These tools are mature enough and have stable versions, so you can safely use them in production.
#8:It is a sort of common cliche that asynchronous code can be used only in CLI scripts. It is perfectly fine to integrate some asynchronous parts in your traditional synchronous environment, even in a traditional web application. For example, you can receive a request, then asynchronously call several different resources and when these calls will be done, you can continue your request-response life-circle and as a result, the page will be rendered faster.
#9:PHP runs in a single thread, which means that at any given moment there is only one bit of PHP code that can be running. That may seem like a limitation, but it brings us a lot of freedom. We don’t have to deal with all this complexity that comes with parallel programming and threaded environment. But at the same time, we have a different set of problems. We have to deal with concurrency. We have to manage and to coordinate it. When we make concurrent requests we say that they “are happening in parallel”. Well, that’s all fine and that’s easy to do, the problems come when we have to sequence the responses. When one request needs information from another one. So, it is the coordination of concurrency that makes our job difficult. And we have a number of different ways to coordinate the concurrency.
#10:
Important pour gérer la concurrence
Chain response
Dont care on time it takes
Time independant wrapper, hides its states
We continue to reason about the value the same way, regardless of whether it’s here or not.
#11:In PHP generators provide the language-level support for functions that can be paused and then resumed.
Generators
Function that can stop time : nside this generator everything stops, it’s like a small blocking program. But outside of this program everything else continues running.
Cerveau human fondamentalement sequentiel
Making asynchronous code readable
Make asynchronous code look ‘sequentiel & synchronous’ for our brains
#12:
Recoil aims to ease development of asynchronous applications by presenting asynchronous control flow in a familiar "imperative" syntax.
https://github.com/recoilphp/recoil
you can use RecoilPHP to rewrite promise chains so they will start looking like a traditional synchronous code.
Coroutines are essentially functions that can be suspended and resumed while maintaining their state. This is useful in asynchronous applications, as the coroutine can suspend while waiting for some task to complete or information to arrive, and the CPU is free to perform other tasks.
PHP generators provide the language-level support for functions that can suspend and resume, and Recoil provides the glue that lets us use these features to perform asynchronous operations.
A Recoil application is started by executing an "entry-point" generator, a little like the main() function in the C programming language. The Recoil kernel inspects the values yielded by the generator and identifies an operation to perform. For example, yielding a float with value 30 causes the coroutine to suspend execution for 30 seconds.
#14:Production Ready
Stable LTS release
No extensions needed
Technology neutral
Supports legacy PHP 5.3+ and HHVM for maximum compatibility.
Carefully tested (unit & functional). PSR
Robust ecosystem – hundreds of 3rd party libraries
Event loop based on the REACTOR PATTERN
#15:1. Reactor
A Reactor runs in a separate thread, and its job is to react to IO events by dispatching the work to the appropriate handler. It’s like a telephone operator in a company who answers calls from clients and transfers the line to the appropriate contact.
2. Handlers
A Handler performs the actual work to be done with an I/O event, similar to the actual officer in the company the client wants to speak to.
A reactor responds to I/O events by dispatching the appropriate handler. Handlers perform non-blocking actions.
You register an event: subscribe to it and start listening. Then you get notified when this event is fired, so you can react to this event via a handler and execute some code. Every iteration of the loop is called a tick. When there are no more listeners in the loop, the loop finishes.
strongly inspired by libraries such as EventMachine (Ruby), Twisted (Python) and Node.js (V8).
The Reactor Pattern is a design pattern for synchronous demultiplexing and order of events as they arrive.
#16:An event loop with low-level utilities
Streams abstraction
async DNS resolver
network client/server
HTTP client/server and interaction with processes.
#19:Async based libraries to be interoperable, they need to use the same event loop
Provides a common LoopInterface
Typical applications use a single event loop which is created at the beginning and run at the end of the program.
The loop instance is created at the beginning of the program.
A convenience factory React\EventLoop\Factory::create() is provided by this library which picks the best available loop implementation.
Factory picks the best loop implementation
#20:because a monotonic time source is only available as of PHP 7.3 (hrtime())
While this does not affect many common use cases, this is an important distinction for programs that rely on a high time precision or on systems that are subject to discontinuous time adjustments (time jumps). This means that if you schedule a timer to trigger in 30s on PHP < 7.3 and then adjust your system time forward by 20s, the timer may trigger in 10s. See also addTimer() for more details.
#21:All of the loops support the following features:
File descriptor polling
Timers
Deferred execution of callbacks
While each implementation of the event loop is different, the program itself should not depend on the particular loop implementation. There may be some differences in the exact timing of the execution or the order in which different types of events are executed. But the behavior of the program should not be affected by these differences.
#22:As a rule of thumb, it is usually recommended to let the loop stop only automatically when it no longer has anything to do.
#23:A listener can be added only once
Removesignal ignore signal that are not registered
Advanced! Note that this low-level API is considered advanced usage. Most use cases should probably use the higher-level readable Stream API instead.
addReadStream can be used to register a listener to be notified when a stream is ready to read.
addWriteStream can be used to register a listener to be notified when a stream is ready to write
#26:Every stream at a low level is simply an EventEmitter, which implements some special methods
consistent higher-level abstraction for processing streams of arbitrary data contents and size. While a stream itself is a quite low-level concept, it can be used as a powerful abstraction to build higher-level components and protocols on top.
#27:On the end event the stream is still readable, but on the close event it is in a non-readable mode:
End event should not be confused with the end() method. This event defines a successful end reading from a source stream, while the end() method defines writing a successful end to a destination stream.
Unlike the end event, close event SHOULD be emitted whenever the stream closes, irrespective of whether this happens implicitly due to an unrecoverable error or explicitly when either side closes the stream. If you only want to detect a successful end, you should use the end event instead.
IsrReadable can be used to check if the stream still accepts incoming data events or if it is ended or closed already. Once the stream is non-readable, no further data or end events SHOULD be emitted.
#28:The drain event will be emitted whenever the write buffer became full previously and is now ready to accept more data.
The pipe event will be emitted whenever a readable stream is pipe()d into this stream.
Close event SHOULD be emitted once or never at all, depending on whether the stream ever terminates. It SHOULD NOT be emitted after a previous close event.
The close(): void method can be used to close the stream (forcefully).
#29:Built on top of the existing interfaces for readable and writable streams and follows the exact same method and event semantics
#30:ReactPHP uses the concept of "streams" throughout its ecosystem, so that many higher-level consumers of this package only deal with stream usage. This implies that stream instances are most often created within some higher-level components and many consumers never actually have to deal with creating a stream instance.
Use react/socket if you want to accept incoming or establish outgoing plaintext TCP/IP or secure TLS socket connection streams.
Use react/http if you want to receive an incoming HTTP request body streams.
Use react/child-process if you want to communicate with child processes via process pipes such as STDIN, STDOUT, STDERR etc.
Use experimental react/filesystem if you want to read from / write to the filesystem.
#31:The ThroughStream implements the DuplexStreamInterface and will simply pass any data you write to it through to its readable end.
The CompositeStream implements the DuplexStreamInterface and can be used to create a single duplex stream from two individual streams implementing ReadableStreamInterface and WritableStreamInterface respectivel
#34:A Deferred represents a computation or unit of work that may not have completed yet. Typically (but not always), that computation will be something that executes asynchronously and completes at some point in the future.
While a deferred represents the computation itself, a Promise represents the result of that computation. Thus, each deferred has a promise that acts as a placeholder for its actual result.
#35:unfulfilled - the promise starts with this state because the value of the deferred is yet unknown
fulfilled - the promise is filled with the value returned from the deferred
failed - there was an exception during the deferred execution.
Once in the fulfilled or rejected state, a promise becomes immutable. Neither its state nor its result (or error) can be modified
#36:A promise represents an eventual outcome, which is either fulfillment (success) and an associated value, or rejection (failure) and an associated reason.
Once in the fulfilled or rejected state, a promise becomes immutable. Neither its state nor its result (or error) can be modified.
#38:Promises can be chained, when the return value of each promise is forwarded to the next promise in the chain. That means that the next promise in the chain will be invoked with this resolved value.
your progress handler MUST return a progress event to be propagated to the next link in the chain. If you return nothing, null will be propagated.
#45:Whenever you have to wait for something (network, filesystem input/output operations) - consider ReactPHP. You don’t have to use it but consider. All these tools allow to start or to defer operations and to get a notification whenever something interesting happens. The main loop is the only thing that is going to be blocking. It has to check for the events, so it could react for the incoming data. When we execute for example sleep(10), the loop will not be executed during these 10 seconds. And everything that loop is going to do during this time will be delayed by these seconds. Never block the loop, for situations when you need to wait, you should use timers.
Timers can be used to execute some code in a delayed future. This code may be executed after the specified interval. Each timer is being executed in the same thread as the whole event loop, so any timer can affect this loop. Timers can be useful for non-blocking operations such as I/O, but executing a long living code in them can lead to the unexpected results.
Also, everything that could take longer than about one millisecond should be reconsidered. When you cannot avoid using blocking functions the common recommendation is to fork this process, so you can continue running the event loop without any delays.
#46:Don’t be afraid to learn new language paradigm. PHP is much more than run the script, execute some code and die. You will be amazed to use your familiar PHP language in a completely new way, in the way you have never used it! Asynchronous code and event-driven programming will expand the way you think about PHP and how this language can be used. There is no need to learn a new language to write asynchronous applications just because someone blames PHP that it is not a right tool for it or because this is how I’ve always done it, it couldn’t possibly be improved on. Just give it a try!