{"id":18462669,"url":"https://github.com/extism/php-sdk","last_synced_at":"2025-04-08T07:32:21.584Z","repository":{"id":194639384,"uuid":"691134984","full_name":"extism/php-sdk","owner":"extism","description":"Extism PHP Host SDK - easily run WebAssembly modules / plugins from PHP applications","archived":false,"fork":false,"pushed_at":"2024-12-16T14:56:18.000Z","size":2293,"stargazers_count":24,"open_issues_count":0,"forks_count":4,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-03-23T08:42:01.698Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://extism.org","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/extism.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-09-13T15:09:29.000Z","updated_at":"2025-03-11T07:54:41.000Z","dependencies_parsed_at":null,"dependency_job_id":"627c5810-b975-444b-a493-945a492b288a","html_url":"https://github.com/extism/php-sdk","commit_stats":null,"previous_names":["extism/php-sdk"],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/extism%2Fphp-sdk","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/extism%2Fphp-sdk/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/extism%2Fphp-sdk/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/extism%2Fphp-sdk/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/extism","download_url":"https://codeload.github.com/extism/php-sdk/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247796295,"owners_count":20997545,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-06T09:03:57.984Z","updated_at":"2025-04-08T07:32:21.567Z","avatar_url":"https://github.com/extism.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Extism PHP Host SDK\n\nThis repo houses the PHP SDK for integrating with the [Extism](https://extism.org/) runtime. Install this library into your host PHP applications to run Extism plugins.\n\n## Installation\n\n### Install the Extism Runtime Dependency\n\nFor this library, you first need to install the Extism Runtime. You can [download the shared object directly from a release](https://github.com/extism/extism/releases) or use the [Extism CLI](https://github.com/extism/cli) to install it:\n\n```bash\nsudo extism lib install latest\n\n#=\u003e Fetching https://github.com/extism/extism/releases/download/v0.5.2/libextism-aarch64-apple-darwin-v0.5.2.tar.gz\n#=\u003e Copying libextism.dylib to /usr/local/lib/libextism.dylib\n#=\u003e Copying extism.h to /usr/local/include/extism.h\n```\n\n\u003e **Note**: This library has breaking changes and targets 1.0 of the runtime. For the time being, install the runtime from our nightly development builds on git: `sudo extism lib install --version git`.\n\n### Install the Package\n\nInstall via [Packagist](https://packagist.org/):\n```sh\ncomposer require extism/extism\n```\n\n*Note*: For the time being you may need to add a minimum-stability of \"dev\" to your composer.json\n```json\n{\n   \"minimum-stability\": \"dev\",\n}\n```\n\n## Getting Started\n\nThis guide should walk you through some of the concepts in Extism and this PHP library.\n\nFirst you should add a using statement for Extism:\n\n```php\nuse Extism\\Plugin;\nuse Extism\\Manifest;\nuse Extism\\Manifest\\UrlWasmSource;\n```\n\n## Creating A Plug-in\n\nThe primary concept in Extism is the [plug-in](https://extism.org/docs/concepts/plug-in). You can think of a plug-in as a code module stored in a `.wasm` file.\n\nSince you may not have an Extism plug-in on hand to test, let's load a demo plug-in from the web:\n\n```php\n$wasm = new UrlWasmSource(\"https://github.com/extism/plugins/releases/latest/download/count_vowels.wasm\");\n$manifest = new Manifest($wasm);\n\n$plugin = new Plugin($manifest, true);\n```\n\n\u003e **Note**: The schema for this manifest can be found here: https://extism.org/docs/concepts/manifest/\n\n### Calling A Plug-in's Exports\n\nThis plug-in was written in Rust and it does one thing, it counts vowels in a string. As such, it exposes one \"export\" function: `count_vowels`. We can call exports using `Plugin.call`:\n\n```php\n$output = $plugin-\u003ecall(\"count_vowels\", \"Hello, World!\");\n\n// =\u003e {\"count\": 3, \"total\": 3, \"vowels\": \"aeiouAEIOU\"}\n```\n\nAll exports have a simple interface of optional bytes in, and optional bytes out. This plug-in happens to take a string and return a JSON encoded string with a report of results.\n\n### Plug-in State\n\nPlug-ins may be stateful or stateless. Plug-ins can maintain state b/w calls by the use of variables. Our count vowels plug-in remembers the total number of vowels it's ever counted in the \"total\" key in the result. You can see this by making subsequent calls to the export:\n\n```php\n$output = $plugin-\u003ecall(\"count_vowels\", \"Hello, World!\");\n// =\u003e {\"count\": 3, \"total\": 6, \"vowels\": \"aeiouAEIOU\"}\n\n$output = $plugin-\u003ecall(\"count_vowels\", \"Hello, World!\");\n// =\u003e {\"count\": 3, \"total\": 9, \"vowels\": \"aeiouAEIOU\"}\n```\n\nThese variables will persist until this plug-in is freed or you initialize a new one.\n\n### Configuration\n\nPlug-ins may optionally take a configuration object. This is a static way to configure the plug-in. Our count-vowels plugin takes an optional configuration to change out which characters are considered vowels. Example:\n\n```php\n$wasm = new UrlWasmSource(\"https://github.com/extism/plugins/releases/latest/download/count_vowels.wasm\");\n\n$manifest = new Manifest($wasm);\n\n$plugin = new Plugin($manifest, true);\n$output = $plugin-\u003ecall(\"count_vowels\", \"Yellow, World!\");\n// =\u003e {\"count\": 3, \"total\": 3, \"vowels\": \"aeiouAEIOU\"}\n\n$manifest = new Manifest($wasm);\n$manifest-\u003econfig-\u003evowels = \"aeiouyAEIOUY\";\n\n$plugin = new Plugin($manifest, true);\n$output = $plugin-\u003ecall(\"count_vowels\", \"Yellow, World!\");\n// =\u003e {\"count\": 4, \"total\": 4, \"vowels\": \"aeiouAEIOUY\"}\n```\n\n### Host Functions\n\n\u003e **Note**\n\u003e\n\u003e Host Functions support is experimental. Due to usage of callbacks with FFI, It may leak memory.\n\nLet's extend our count-vowels example a little bit: Instead of storing the `total` in an ephemeral plug-in var, let's store it in a persistent key-value store!\n\nWasm can't use our KV store on it's own. This is where `Host Functions` come in.\n\n[Host functions](https://extism.org/docs/concepts/host-functions) allow us to grant new capabilities to our plug-ins from our application. They are simply some PHP functions you write which can be passed down and invoked from any language inside the plug-in.\n\nLet's load the manifest like usual but load up this `count_vowels_kvstore` plug-in:\n\n```php\n$manifest = new Manifest(new UrlWasmSource(\"https://github.com/extism/plugins/releases/latest/download/count_vowels_kvstore.wasm\"));\n```\n\n\u003e *Note*: The source code for this is [here](https://github.com/extism/plugins/blob/main/count_vowels_kvstore/src/lib.rs) and is written in rust, but it could be written in any of our PDK languages.\n\nUnlike our previous plug-in, this plug-in expects you to provide host functions that satisfy our import interface for a KV store.\n\nWe want to expose two functions to our plugin, `void kv_write(key string, value byte[])` which writes a bytes value to a key and `byte[] kv_read(key string)` which reads the bytes at the given `key`.\n\n```php\n// pretend this is Redis or something :)\n$kvstore = [];\n$kvRead = new HostFunction(\"kv_read\", [ExtismValType::I64], [ExtismValType::I64], function (string $key) use (\u0026$kvstore) {\n    $value = $kvstore[$key] ?? \"\\0\\0\\0\\0\";\n\n    echo \"Read \" . bytesToInt($value) . \" from key=$key\" . PHP_EOL;\n    return $value;\n});\n\n$kvWrite = new HostFunction(\"kv_write\", [ExtismValType::I64, ExtismValType::I64], [], function (string $key, string $value) use (\u0026$kvstore) {\n    echo \"Writing value=\" . bytesToInt($value) . \" from key=$key\" . PHP_EOL;\n    $kvstore[$key] = $value;\n});\n\nfunction bytesToInt(string $bytes): int {\n    $result = unpack(\"L\", $bytes);\n    return $result[1];\n}\n```\n\n\u003e *Note*: The plugin provides memory pointers, which the SDK automatically converts into a `string`. Similarly, when a host function returns a `string`, the SDK allocates it in the plugin memory and provides a pointer back to the plugin. For manual memory management, request `CurrentPlugin` as the first parameter of the host function. For example:\n\u003e\n\u003e ```php\n\u003e $kvRead = new HostFunction(\"kv_read\", [ExtismValType::I64], [ExtismValType::I64], function (CurrentPlugin $p, int $keyPtr) use ($kvstore) {\n\u003e   $key = $p-\u003eread_block($keyPtr);\n\u003e \n\u003e   $value = $kvstore[$key] ?? \"\\0\\0\\0\\0\";\n\u003e \n\u003e   return $p-\u003ewrite_block($value);\n\u003e });\n\u003e ```\n\nWe need to pass these imports to the plug-in to create them. All imports of a plug-in must be satisfied for it to be initialized:\n\n```php\n$plugin = new Plugin($manifest, true, [$kvRead, $kvWrite]);\n\n$output = $plugin-\u003ecall(\"count_vowels\", \"Hello World!\");\n\necho($output . PHP_EOL);\n// =\u003e Read 0 from key=count-vowels\"\n// =\u003e Writing value=3 from key=count-vowels\"\n// =\u003e {\"count\": 3, \"total\": 3, \"vowels\": \"aeiouAEIOU\"}\n\n$output = $plugin-\u003ecall(\"count_vowels\", \"Hello World!\");\n\necho($output . PHP_EOL);\n// =\u003e Read 3 from key=count-vowels\"\n// =\u003e Writing value=6 from key=count-vowels\"\n// =\u003e {\"count\": 3, \"total\": 6, \"vowels\": \"aeiouAEIOU\"}\n```\n\nFor host function callbacks, these are the valid parameter types:\n - `CurrentPlugin`: Only if its the first parameter. Allows you to manually manage memory. Optional.\n - `string`: If the parameter represents a memory offset (an `i64`), then the SDK can automatically load the buffer into a `string` for you.\n - `int`: For `i32` and `i64` parameters.\n - `float`: For `f32` and `f64` parameters.\n\nValid return types:\n - `void`\n - `int`: For `i32` and `i64` parameters.\n - `float`: For `f32` and `f64` parameters.\n - `string`: the content of the string will be allocated in the wasm plugin memory and the offset (`i64`) will be returned.\n\n### Fuel Limits\n\nPlugins can be initialized with a fuel limit to constrain their execution. When a plugin runs out of fuel, it will throw an exception. This is useful for preventing infinite loops or limiting resource usage.\n\n```php\n// Create plugin with fuel limit of 1000 instructions\n$plugin = new Plugin($manifest, true, [], new PluginOptions(true, 1000));\n\ntry {\n    $output = $plugin-\u003ecall(\"run_test\", \"\");\n} catch (\\Exception $e) {\n    // Plugin ran out of fuel\n    // The exception message will contain \"fuel\"\n}\n```\n\n### Call Host Context\n\nCall Host Context provides a way to pass per-call context data when invoking a plugin function. This is useful when you need to provide data specific to a particular function call rather than data that persists across all calls.\n\nHere's an example of using call host context to implement a multi-user key-value store where each user has their own isolated storage:\n\n```php\n$multiUserKvStore = [[]];\n\n$kvRead = new HostFunction(\"kv_read\", [ExtismValType::I64], [ExtismValType::I64], function (CurrentPlugin $p, string $key) use (\u0026$multiUserKvStore) {\n    $userId = $p-\u003egetCallHostContext(); // get a copy of the host context data\n    $kvStore = $multiUserKvStore[$userId] ?? [];\n\n    return $kvStore[$key] ?? \"\\0\\0\\0\\0\";\n});\n\n$kvWrite = new HostFunction(\"kv_write\", [ExtismValType::I64, ExtismValType::I64], [], function (CurrentPlugin $p, string $key, string $value) use (\u0026$multiUserKvStore) {\n    $userId = $p-\u003egetCallHostContext(); // get a copy of the host context data\n    $kvStore = $multiUserKvStore[$userId] ?? [];\n\n    $kvStore[$key] = $value;\n    $multiUserKvStore[$userId] = $kvStore;\n});\n\n$plugin = self::loadPlugin(\"count_vowels_kvstore.wasm\", [$kvRead, $kvWrite]);\n\n$userId = 1;\n\n$response = $plugin-\u003ecallWithContext(\"count_vowels\", \"Hello World!\", $userId);\n$this-\u003eassertEquals('{\"count\":3,\"total\":3,\"vowels\":\"aeiouAEIOU\"}', $response);\n\n$response = $plugin-\u003ecallWithContext(\"count_vowels\", \"Hello World!\", $userId);\n$this-\u003eassertEquals('{\"count\":3,\"total\":6,\"vowels\":\"aeiouAEIOU\"}', $response);\n```\n\nNote: Unlike some other language SDKS, in the Extism PHP SDK the host context is copied when accessed via `getCallHostContext()`. This means that modifications to the context object within host functions won't affect the original context object passed to `callWithContext()`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fextism%2Fphp-sdk","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fextism%2Fphp-sdk","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fextism%2Fphp-sdk/lists"}