PHPackages                             markhuot/synapse - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. markhuot/synapse

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

markhuot/synapse
================

A neural-level connection between JavaScript and PHP

1.0.6(1y ago)19MITTypeScriptCI passing

Since Feb 22Pushed 1y ago1 watchersCompare

[ Source](https://github.com/markhuot/synapse)[ Packagist](https://packagist.org/packages/markhuot/synapse)[ RSS](/packages/markhuot-synapse/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (7)Dependencies (2)Versions (8)Used By (0)

Synapse
=======

[](#synapse)

```
$ composer install markhuot/synapse
$ npm install --save @markhuot/synapse
```

Write PHP code directly in Javascript and seamlessly transition data across the http boundary from client to server and back again.

```
import {php} from "@markhuot/synapse/php";

const toggleTodo = async (todoId) => `php
    $todo = \App\Models\Todo::findOrFail(${todoId});
    auth()->user()->can('update', $todo);

    $todo->complete = !$todo->complete;
    $todo->save();
`.execute()

const deleteTodo = async (todoId) => {
    if (! confirm('Are you sure you want to delete this todo?')) {
        return;
    }

    await php`
        $todo = \App\Models\Todo::findOrFail(${todoId});
        auth()->user()->can('delete', $todo);

        $todo->delete();
    `.execute();
}

export default function Todo({ todo }) {
    return
         toggleTodo(todo.id)}/>
        {todo.title}
         deleteTodo(todo.id)}>
            Delete

    ;
}
```

Getting started
---------------

[](#getting-started)

You need to configure both sides of the client/server boundary to utilize Synapse. First, let's configure the server side.

Note

If you're using Laravel the server-side is automatically configured when you run `composer require markhuot/synapse`. You can skip to the client-side setup.

### Server-side

[](#server-side)

Synapse expects an HTTP handler listening at `/synapse`, by default. You can configure this on the front-end to another URI, if desired. However you're responding to routes, you'll need to process the POST data with `Synapse\Actions\HandleSynapseRequest`. Here's a basic example assuming this is inside a file at `/synapse/index.php`.

```
$action = new Synapse\Actions\HandleSynapseRequest(
    path: PROJECT_ROOT.'/.synapse/handlers/'
);

$result = $action($_POST['_payloads']);

echo json_encode($result);
```

The result of a Synapse handler is variable and dependent on the code you write within your Javascript files. In this example we blindly convert to JSON. In reality the result may be more nuanced and you'll need to account for things like redirects, headers, etc.

### Client-side

[](#client-side)

Synapse works with any front-end framework that integrates with Vite as the Synapse compiler is written as a Vite plugin. Configuration happens in your `vite.config.js` file by adding the plugin function in to your definition,

```
import {synapse} from 'synapse/vite';

export default defineConfig({
    plugins: [
        synapse({
            include: [/\.tsx$/],
            exclude: [/node_modules/],
            synapsePath: '.synapse'
        }),
    ],
});
```

Every configuration key is optional and by default Synapse will scan through all files run through Vite for `php` tagged templates.

Lastly, you need to configure your HTTP handler. Within a Laravel application this is typically done in `resources/js/app.js` but any entrypoint will do.

here is an example of configuring Synapse with Inertia/React,

```
import { router } from '@inertiajs/react';
import { setRequestHandler } from 'synapse/php';

setRequestHandler((options) => {
  return router.post(options.url, options.body, options);
})
```

If you do not set a request handler a default handler will make a request to `/synapse` with a correct POST body.

How it works
------------

[](#how-it-works)

When Vite compiles your JS it removes any `php` tagged template strings and moves each template string in to its own `.php` file.

As the PHP code is moved to its own file the JS file is updated with a pseudo random hash. The raw PHP is removed. This means your PHP code is never sent to the client and is not inspectable.

Note

The hashes generated for each template string are pseudo random and are not reproducible. Because of that you should not share generated php files across computers. The .synapse directory should be .gitignored and re-generated in dev, staging, and production--just like you would your compiled JS.

When the client calls a `php` tagged template string: instead of parsing the string, it makes a HTTP request to your back-end with a payload containing a random hash identifying the block(s) of PHP to run.

The real magic is in how Javascript handles tagged template strings. Here's a very. brief example,

```
const firstName = 'Michael';
const lastName = 'Bluth';
php`echo "${lastName}, ${firstName}";`
```

In that example the synapse/php template gets called with string parameters matching `"echo \""`, `", "` and `"\";"`. It also gets called with value parameters matching the values of `lastName` and `firstName`.

On the client-side we ignore the strings entirely (in fact Vite replaces them out with empty strings so your PHP is not leaked). Then, we can pull out the values and send just the values across the HTTP boundary to the server.

That leaves us with the above source JS that compiles to the following compiled JS (where `ahufduah` is a pseudo random hash generated during the compilation step).

```
const firstName = 'Michael';
const lastName = 'Bluth';
php`ahufduah${lastName}${firstName}`
```

The compiled PHP looks like this, after swapping out the JS variables for PHP variables:

```
