{"id":20608919,"url":"https://github.com/tpunt/pht","last_synced_at":"2025-07-14T09:13:39.827Z","repository":{"id":82886533,"uuid":"115046368","full_name":"tpunt/pht","owner":"tpunt","description":"A new threading extension for PHP","archived":false,"fork":false,"pushed_at":"2018-04-11T13:33:52.000Z","size":192,"stargazers_count":179,"open_issues_count":5,"forks_count":14,"subscribers_count":10,"default_branch":"master","last_synced_at":"2025-03-28T15:51:49.797Z","etag":null,"topics":["concurrency","multithreading","parallelism","php","php-extension","pthreads","threading"],"latest_commit_sha":null,"homepage":"","language":"C","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/tpunt.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":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-12-21T20:55:50.000Z","updated_at":"2025-03-07T20:07:18.000Z","dependencies_parsed_at":"2023-04-12T19:31:08.800Z","dependency_job_id":null,"html_url":"https://github.com/tpunt/pht","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tpunt%2Fpht","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tpunt%2Fpht/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tpunt%2Fpht/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tpunt%2Fpht/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tpunt","download_url":"https://codeload.github.com/tpunt/pht/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249004860,"owners_count":21196967,"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":["concurrency","multithreading","parallelism","php","php-extension","pthreads","threading"],"created_at":"2024-11-16T10:12:15.070Z","updated_at":"2025-04-15T04:24:23.589Z","avatar_url":"https://github.com/tpunt.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# The Pht Threading Extension\n\nThis extension exposes a new approach to threading in PHP.\n\nQuick feature list:\n - Classes, functions, and files may be threaded\n - The inter-thread communication (ITC) data structures include: hash table, queue, vector\n - Threads are reusable for any number of tasks\n\nRequirements:\n - A ZTS version of PHP 7.2. The master branch of php-src is not currently compatible\n\nAny Unix-based OS is supported (including OS X), along with Windows. This extension was explicitly tested on OS X (Yosemite and Sierra), Ubuntu 14.04 (32bit), and Windows Server 2012 (the pthreads-win32 library is needed).\n\nDocumentation: [php.net/pht](http://php.net/pht)\n\nContents:\n - [Installation](https://github.com/tpunt/pht#installation)\n - [Pthreads VS pht](https://github.com/tpunt/pht#pthreads-vs-pht)\n - [The Basics](https://github.com/tpunt/pht#the-basics)\n - [API](https://github.com/tpunt/pht#api)\n - [Quick Examples](https://github.com/tpunt/pht#quick-examples)\n   - [Threading Types](https://github.com/tpunt/pht#threading-types)\n     - [Class Threading](https://github.com/tpunt/pht#class-threading)\n     - [Function Threading](https://github.com/tpunt/pht#function-threading)\n     - [File Threading](https://github.com/tpunt/pht#file-threading)\n   - [Inter-Thread Communication Data Structures](https://github.com/tpunt/pht#inter-thread-communication-data-structures)\n     - [Queue](https://github.com/tpunt/pht#queue)\n     - [Vector](https://github.com/tpunt/pht#vector)\n     - [Hash Table](https://github.com/tpunt/pht#hash-table)\n   - [Atomic Values](https://github.com/tpunt/pht#atomic-values)\n     - [Atomic Integer](https://github.com/tpunt/pht#atomic-integer)\n\nThis extension was built using a few ideas from the [pthreads](https://github.com/krakjoe/pthreads) extension. I'd therefore like to give credit to, as well as thank, [Joe Watkins](https://github.com/krakjoe) for his great work on the pthreads project!\n\n## Installation\n\nIf you're using a Unix-based OS, then you can build the extension from source by:\n```php\ngit clone https://github.com/tpunt/pht\ncd pht\ngit checkout tags/v0.0.1\nphpize\n./configure\nmake\nmake install\n```\n\nIf you're using Windows, see the [release page](https://github.com/tpunt/pht/releases) for the appropriate .dll extension file. The pthreadVC2.dll file (distributed alongside the extension's .dll file) will need to be made available in your `PATH` environment variable.\n\nOnce the .so or .dll file has been acquire, the php.ini file will then need to be updated (to load the extension) with:\n```\nextension=\"path/to/pht_file\"\n```\n\n## Pthreads vs pht\n\nBoth extensions have their own advantages and disadvantages.\n\nPthreads advantages over pht:\n - Uses synchronised blocks over mutex locks (which can be cleaner to use and harder to mess up)\n - Easier communication between threads (no knowledge of data structures is required, which PHP developers tend to lack in)\n - Maturer\n\nPht advantages over pthreads:\n - No serialisation of properties on threaded objects, meaning:\n   - No performance hit when reading from or writing to properties\n   - No unfamiliar semantics, such as implicitly casting arrays to `Volatile` objects or property immutability for some types\n   - No limitations on what types of data can be stored as properties on threaded objects\n - The ability to thread functions and files (as well as classes)\n - Threaded classes implement a `Runnable` interface, rather than having to inherit (so more flexibility on inheritance)\n\n## The Basics\n\nThis approach to threading abstracts away the thread itself behind a dedicated object (`Thread`), where tasks are added to that thread's internal task queue.\n\nExample:\n```php\n\u003c?php\n\nuse pht\\{Thread, Runnable};\n\nclass Task implements Runnable\n{\n    public function run() {}\n}\n\n$thread = new Thread();\n\n// Thread::addClassTask(string $className, mixed ...$constructorArgs) : void;\n$thread-\u003eaddClassTask(Task::class);\n\n// Thread::addFunctionTask(callable $fn, mixed ...$fnArgs) : void;\n$thread-\u003eaddFunctionTask(function ($zero) {var_dump($zero);}, 0);\n\n// Thread::addFileTask(string $filename, mixed ...$globals) : void;\n$thread-\u003eaddFileTask('some_file.php', 1, 2, 3);\n\n$thread-\u003estart();\n$thread-\u003ejoin();\n```\n\n`some_file.php`:\n```php\n\u003c?php\n\n[$one, $two, $three] = $_THREAD;\n```\n\nThe task types have the following properties:\n - Class tasks will be instantiated inside of the new thread\n - Function tasks will be serialised and passed into the new thread\n - File tasks will open the file inside of the new thread\n\nAll of these tasks will execute in isolation. In particular, for class tasks, it means the spawned objects cannot be passed around between threads. By keeping the threading contexts completely separate from one-another, we prevent the need to serialise the properties of threaded objects (a necessary evil if such objects had to operate in multiple threads, as seen in pthreads).\n\nGiven the isolation of threaded contexts, we have a new problem: how can data be passed between threads for inter-thread communication (ITC)? To solve this problem, threadable data structures have been implemented, where mutex locks have been exposed to the programmer for controlling access to them. Whilst this has increased the complexity a bit for the programmer, it has also increased the flexibility, too.\n\nSo far, the following data structures have been implemented: queue, hash table, and vector. These data structures can be safely passed around between threads, and manipulated by multiple threads using the mutex locks that have been packed in with the data structure. They are reference-counted across threads, and so they do not need to be explicitly destroyed.\n\nWith this approach to threading, only the given built-in data structures need to be safely passed around between threads.\n\nThis means that the serialisation points to be aware of are:\n - The arguments being passed to `Thread::addClassTask()`, `Thread::addFunctionTask()`, and `Thread::addFileTask()`\n - The values being placed into the ITC-based data structures\n\n## API\n\n```php\n\u003c?php\n\nnamespace pht;\n\nclass Thread\n{\n    public function addClassTask(string $className, mixed ...$ctorArgs) : void;\n    public function addFunctionTask(callable $fn, mixed ...$fnArgs) : void;\n    public function addFileTask(string $filename, mixed ...$globals) : void;\n    public function taskCount(void) : int;\n    public function start(void) : void;\n    public function join(void) : void;\n}\n\ninterface Runnable\n{\n    public function run(void) : void;\n}\n\n// internal interface, not implementable by userland PHP classes\ninterface Threaded\n{\n    public function lock(void) : void;\n    public function unlock(void) : void;\n}\n\nfinal class Queue implements Threaded\n{\n    public function push(mixed $value) : void;\n    public function pop(void) : mixed;\n    public function front(void) : mixed;\n    public function lock(void) : void;\n    public function unlock(void) : void;\n    public function size(void) : int;\n}\n\nfinal class HashTable implements Threaded\n{\n    public function lock(void) : void;\n    public function unlock(void) : void;\n    public function size(void) : int;\n    // ArrayAccess API is enabled, but the userland interface is not explicitly implemented\n}\n\nfinal class Vector implements Threaded\n{\n    public function __construct([int $size = 0 [, mixed $defaultValue = 0]]);\n    public function resize(int $size [, mixed $defaultValue = 0]) : void;\n    public function push(mixed $value) : void;\n    public function pop(void) : mixed;\n    public function shift(void) : mixed;\n    public function unshift(mixed $value) : void;\n    public function insertAt(mixed $value, int $index) : void;\n    public function updateAt(mixed $value, int $index) : void;\n    public function deleteAt(int $index) : void;\n    public function lock(void) : void;\n    public function unlock(void) : void;\n    public function size(void) : int;\n    // ArrayAccess API is enabled, but the userland interface is not explicitly implemented\n}\n\nfinal class AtomicInteger implements Threaded\n{\n    public function __construct([int $value = 0]);\n    public function get(void) : int;\n    public function set(int $value) : void;\n    public function inc(void) : void;\n    public function dec(void) : void;\n    public function lock(void) : void;\n    public function unlock(void) : void;\n}\n```\n\n## Quick Examples\n\nThis section demonstrates some quick examples of the basic features. For generic examples, see the [examples](https://github.com/tpunt/pht/tree/master/examples) folder instead. And for further information on the classes and their methods, checkout the [documentation](http://php.net/pht).\n\n### Threading Types\n\n#### Class Threading\n\nClasses that will be threaded need to implement the `Runnable` interface. The implemented `Runnable::run` method acts as the entry point of execution for when the class task is executed.\n\n```php\n\u003c?php\n\nuse pht\\{Thread, Runnable};\n\nclass Task implements Runnable\n{\n    private $one;\n\n    public function __construct(int $one)\n    {\n        $this-\u003eone = $one;\n    }\n\n    public function run()\n    {\n        var_dump($this-\u003eone);\n    }\n}\n\n$thread = new Thread();\n\n$thread-\u003eaddClassTask(Task::class, 1);\n\n$thread-\u003estart();\n$thread-\u003ejoin();\n```\n\nAll `$ctorArgs` being passed into the `Thread::addClassTask()` method will be serialised.\n\n#### Function Threading\n\nFunctions must not refer to `$this` (it will become `null` in the threaded context), and must not import variables from their outer scope (via the `use` statement). They should be completely standalone.\n\n```php\n\u003c?php\n\nuse pht\\Thread;\n\nclass Test\n{\n    public static function run(){var_dump(5);}\n    public static function run2(){var_dump(6);}\n}\n\nfunction aFunc(){var_dump(3);}\n\n$thread = new Thread();\n\n$thread-\u003eaddFunctionTask(static function($one) {var_dump($one);}, 1);\n$thread-\u003eaddFunctionTask(function() {var_dump(2);});\n$thread-\u003eaddFunctionTask('aFunc');\n$thread-\u003eaddFunctionTask('array_map', function ($n) {var_dump($n);}, [4]);\n$thread-\u003eaddFunctionTask(['Test', 'run']);\n$thread-\u003eaddFunctionTask([new Test, 'run2']);\n\n$thread-\u003estart();\n$thread-\u003ejoin();\n```\n\nAll `$fnArgs` being passed into the `Thread::addFunctionTask()` method will be serialised.\n\n#### File Threading\n\nTo pass data to the file being threaded, pass them as additional arguments to the `Thread::addFileTask()` method. They will then become available in a special `$_THREAD` superglobals array inside of the threaded file.\n\n```php\n\u003c?php\n\nuse pht\\Thread;\n\n$thread = new Thread();\n\n$thread-\u003eaddFileTask('file.php', 1, 2, 3);\n\n$thread-\u003estart();\n$thread-\u003ejoin();\n```\n\n`file.php`\n```php\n\u003c?php\n\n[$one, $two, $three] = $_THREAD;\n\nvar_dump($one, $two, $three);\n```\n\nAll `$globals` being passed into the `Thread::addFileTask()` method will be serialised.\n\n### Inter-Thread Communication Data Structures\n\nThe inter-thread communication (ITC) data structures enable for a two-way communication style between threads.\n\nThey are:\n - safe to pass around between threads\n - reference-counted across threads\n - nestable within one-another (such as using a `Vector` in a `Vector`)\n\nThings to note:\n - cyclic references will leak (due to the simplistic nature of reference counting)\n - all values placed into these data structures will be serialised\n - the mutexes exposed by these data structures are not reentrant\n\n#### Queue\n\n```php\n\u003c?php\n\nuse pht\\{Thread, Queue};\n\n$thread = new Thread();\n$queue = new Queue();\n$queueItemCount = 5;\n\n$thread-\u003eaddFunctionTask(function ($queue, $queueItemCount) {\n    for ($i = 0; $i \u003c $queueItemCount; ++$i) {\n        $queue-\u003elock();\n        $queue-\u003epush($i);\n        $queue-\u003eunlock();\n    }\n}, $queue, $queueItemCount);\n\n$thread-\u003estart();\n\nwhile (true) {\n    $queue-\u003elock();\n\n    if ($queue-\u003esize() === $queueItemCount) {\n        $queue-\u003eunlock();\n        break;\n    }\n\n    $queue-\u003eunlock();\n}\n\n$thread-\u003ejoin();\n\n// since we are no longer using $queue in multiple threads, we don't need to lock it\nwhile ($queue-\u003esize()) {\n    var_dump($queue-\u003epop());\n}\n```\n\n#### Vector\n\n```php\n\u003c?php\n\nuse pht\\{Thread, Vector};\n\n$thread = new Thread();\n$vector = new Vector();\n$vectorItemCount = 5;\n\nfor ($i = 0; $i \u003c $vectorItemCount; ++$i) {\n    $thread-\u003eaddFunctionTask(function ($vector, $i) {\n        $vector-\u003elock();\n        $vector-\u003epush($i);\n        $vector-\u003eunlock();\n    }, $vector, $i);\n}\n\n$thread-\u003estart();\n\nwhile (true) {\n    $vector-\u003elock();\n\n    if ($vector-\u003esize() === $vectorItemCount) {\n        $vector-\u003eunlock();\n        break;\n    }\n\n    $vector-\u003eunlock();\n}\n\n$thread-\u003ejoin();\n\n// since we are no longer using $vector in multiple threads, we don't need to lock it\nfor ($i = 0; $i \u003c $vectorItemCount; ++$i) {\n    var_dump($vector[$i]);\n}\n```\n\n#### Hash Table\n\n```php\n\u003c?php\n\nuse pht\\{Thread, HashTable};\n\n$thread = new Thread();\n$hashTable = new HashTable();\n$hashTableItemCount = 5;\n\nfor ($i = 0; $i \u003c $hashTableItemCount; ++$i) {\n    $thread-\u003eaddFunctionTask(function ($hashTable, $i) {\n        $hashTable-\u003elock();\n        $hashTable[chr(ord('a') + $i)] = $i;\n        $hashTable-\u003eunlock();\n    }, $hashTable, $i);\n}\n\n$thread-\u003estart();\n\nwhile (true) {\n    $hashTable-\u003elock();\n\n    if ($hashTable-\u003esize() === $hashTableItemCount) {\n        $hashTable-\u003eunlock();\n        break;\n    }\n\n    $hashTable-\u003eunlock();\n}\n\n$thread-\u003ejoin();\n\n// since we are no longer using $hashTable in multiple threads, we don't need to lock it\nfor ($i = 0; $i \u003c $hashTableItemCount; ++$i) {\n    var_dump($hashTable[chr(ord('a') + $i)]);\n}\n```\n\n### Atomic Values\n\nAtomic values are classes that wrap simple values. These values are safe to update without acquiring mutex locks, but they also pack with them mutex locks should multiple operations need to be performed together. The mutex locks, for this reason, are reentrant.\n\n#### Atomic Integer\n\n```php\n\u003c?php\n\nuse pht\\{Thread, AtomicInteger};\n\n$thread = new Thread();\n$atomicInteger = new AtomicInteger();\n$max = 100000;\n\n$thread-\u003eaddFunctionTask(function ($atomicInteger, $max) {\n    for ($i = 0; $i \u003c $max; ++$i) {\n        $atomicInteger-\u003einc();\n    }\n}, $atomicInteger, $max);\n\n$thread-\u003estart();\n\n// safe\nfor ($i = 0; $i \u003c $max; ++$i) {\n    $atomicInteger-\u003einc();\n}\n\n// safe\nwhile ($atomicInteger-\u003eget() !== $max * 2);\n\n// requires mutex locking since we need to perform multiple operations together\n$atomicInteger-\u003elock();\n$atomicInteger-\u003eset($atomicInteger-\u003eget() * 2);\n$atomicInteger-\u003eunlock();\n\n$thread-\u003ejoin();\n\nvar_dump($atomicInteger-\u003eget()); // int(400000)\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftpunt%2Fpht","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftpunt%2Fpht","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftpunt%2Fpht/lists"}