PHP Resonance Framework
Build Copilots, Conversational Applications, IO Intensive Services, and more.
Resonance is designed to build IO-intensive web applications from the ground up, making the development and maintenance as predictable and easy as possible.
It provides AI capabilities through llama.cpp and integration with Open Source foundational LLMs, which makes it a great fit for building conversational applications and copilots.
Takes full advantage of asynchronous PHP.
Get StartedHost Rubix ML machine learning models in production
Rubix ML is a high-level machine learning and deep learning library for the PHP language.
Why Resonance?
-
Predictable Performance
Resonance is designed with a few priorities: no memory leaks, blocking operations, and garbage collector surprises.
Most of the internals are read-only and stateless. After the application startup, nothing disturbs JIT and opcode (Reflection is only used during the application startup), so there are no surprise slowdowns during runtime.
Dependency Injection container is designed to prevent any cyclical dependencies between services.
-
Opinionated
All the libraries under the hood have been thoroughly tested to ensure they work together correctly, complement each other, and work perfectly under async environments.
For example, Resonance implements custom Doctrine drivers, so it uses Swoole's connection pools.
-
Optimized for Input/Output
Resonance is designed to handle IO-intensive tasks, such as serving Machine Learning models, handling WebSocket connections, and processing long-running HTTP requests.
It views modern applications as a mix of services that communicate with each other asynchronously, including AI completions and ML inferences, so it provides a set of tools to make this communication as easy as possible.
-
Real-Time Messaging
Resonance provides a set of tools to build real-time messaging services, including WebSocket and gRPC.
-
All-in-One
Resonance includes everything you need to build a modern web application, from the HTTP server to the AI capabilities.
It provides security features, HTML templating, integration with open-source LLMs, and provides capability to serve ML models.
New Releases
-
Serve Machine Learning Models v0.31.0
Resonance integrates with Rubix ML to serve Machine Learning models in the same codebase as the rest of your application.
Learn More -
Semi-Scripted Conversational Applications tutorial
Large Language Models, when used as a user interface, enable users to create advanced applications that can replace most traditional interfaces.
Learn More -
Dialogue Nodes v0.30.0
Build and maintain copilots, scripted dialogues, semi-scripted dialogues and fully conversational applications.
Learn More -
Extract Data with AI v0.30.0
Extractors either map user's input into a strictly formatted data or extract specific data from user's input.
Learn More -
Function Responders v0.29.0
You can use functions as responders to handle incoming HTTP requests.
Learn More -
Observable Tasks v0.28.0
-
gRPC v0.24.0
Use gRPC to communicate between services.
Learn More -
Constraints Schema v0.23.0
Use constraints to validate incoming data.
Learn More -
Prompt Subject Responders v0.20.0
-
Swoole Server Tasks v0.18.0
Use Swoole tasks to handle long running tasks.
Learn More -
OAuth 2.0 v0.10.0
Use OAuth 2.0 to authenticate users.
Learn More
-
Simple Things Remain Simple
Writing HTTP controllers is similar to how it's done in the synchronous code.
Controllers have new exciting features that take advantage of the asynchronous environment.
Learn More#[RespondsToHttp( method: RequestMethod::GET, pattern: '/', )] function Homepage(ServerRequestInterface $request, ResponseInterface $response): TwigTemplate { return new TwigTemplate('website/homepage.twig'); }
-
Chat with Open-Source LLMs
Create prompt controllers to directly answer user's prompts.
LLM takes care of determining user's intention, you can focus on taking an appropriate action.
Learn More#[RespondsToPromptSubject( action: 'adopt', subject: 'cat', )] #[Singleton(collection: SingletonCollection::PromptSubjectResponder)] readonly class CatAdopt implements PromptSubjectResponderInterface { public function respondToPromptSubject(PromptSubjectRequest $request, PromptSubjectResponse $response): void { // Pipes message through WebSocket... $response->write("Here you go:\n\n"); $response->write(" |\_._/|\n"); $response->write(" | o o |\n"); $response->write(" ( T )\n"); $response->write(" .^`-^-`^.\n"); $response->write(" `. ; .`\n"); $response->write(" | | | | |\n"); $response->write(" ((_((|))_))\n"); $response->end(); } }
-
Serve Machine Learning Models
Resonance integrates with Rubix ML and allows you to serve Machine Learning models in the same codebase as the rest of your application.
Resonance allows you to serve inferences from your models through HTTP, WebSocket, and other protocols.
Learn More#[RespondsToHttp( method: RequestMethod::POST, pattern: '/predict', )] #[Singleton(collection: SingletonCollection::HttpResponder)] readonly class Predict extends HttpResponder { private Estimator $model; public function __construct() { $this->model = PersistentModel::load(new Filesystem(DM_ROOT.'/models/iris.model')); } public function respond( ServerRequestInterface $request, ResponseInterface $response, ): HttpInterceptableInterface { $dataset = new Unlabeled($request->getParsedBody()); $predictions = $this->model->predict($dataset); return new JsonResponse($predictions); } }
-
Asynchronous Where it Matters
Respond asynchronously to incoming RPC or WebSocket messages (or both combined) with little overhead.
You can set up all the asynchronous features using attributes. No elaborate configuration is needed.
Learn More#[RespondsToWebSocketJsonRPC(JsonRPCMethod::Echo)] #[Singleton(collection: SingletonCollection::WebSocketJsonRPCResponder)] final readonly class EchoResponder extends WebSocketJsonRPCResponder { public function getConstraint(): Constraint { return new StringConstraint(); } public function onRequest( WebSocketAuthResolution $webSocketAuthResolution, WebSocketConnection $webSocketConnection, RPCRequest $rpcRequest, ): void { $webSocketConnection->push(new RPCResponse( $rpcRequest, $rpcRequest->payload, )); } }
-
Consistency is Key
You can keep the same approach to writing software no matter the size of your project.
There are no growing central configuration files or service dependencies registries. Every relation between code modules is local to those modules.
Learn More#[ListensTo(HttpServerStarted::class)] #[Singleton(collection: SingletonCollection::EventListener)] final readonly class InitializeErrorReporting extends EventListener { public function handle(object $event): void { // ... } }
-
Powerful Dependency Injection
Your project's files are indexed when the application starts. Relations between services are then set up by using attributes.
There is no need for an elaborate configuration of services. Everything is handled by the attributes.
Learn More#[Singleton(provides: LoggerInterface::class)] readonly class Logger implements LoggerInterface { public function log($level, string|Stringable $message, array $context = []): void { // ... } }
-
Promises in PHP
Resonance provides a partial implementation of Promise/A+ spec to handle various asynchronous tasks.
$future1 = new SwooleFuture(function (int $value) { assert($value === 1); return $value + 2; }); $future2 = $future1->then(new SwooleFuture(function (int $value) { assert($value === 3); return $value + 4; })); assert($future2->resolve(1)->result === 7);
-
GraphQL Out of the Box
You can build elaborate GraphQL schemas by using just the PHP attributes.
Resonance takes care of reusing SQL queries and optimizing the resources' usage.
All fields can be resolved asynchronously.
Learn More#[GraphQLRootField( name: 'blogPosts', type: GraphQLRootFieldType::Query, )] #[Singleton(collection: SingletonCollection::GraphQLRootField)] final readonly class Blog implements GraphQLFieldableInterface { public function __construct( private DatabaseConnectionPoolRepository $connectionPool, private BlogPostType $blogPostType, ) {} public function resolve(): GraphQLReusableDatabaseQueryInterface { return new SelectBlogPosts($this->connectionPool); } public function toGraphQLField(): array { return [ 'type' => new ListOfType($this->blogPostType), 'resolve' => $this->resolve(...), ]; } }