PHP Framework for Next-Gen Web Apps
Connect, Converse, Create
Resonance is designed from the ground up to facilitate interoperability and messaging between services in your infrastructure and beyond. It provides AI capabilities, has a built in web server and integrates with llama.cpp.
Takes full advantage of asynchronous PHP. Built on top of Swoole.
Get StartedNew Releases
-
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
-
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(); } }
-
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(...), ]; } }