{"id":16443784,"url":"https://github.com/olleharstedt/pholyglot","last_synced_at":"2026-04-09T14:47:13.756Z","repository":{"id":58330025,"uuid":"523489195","full_name":"olleharstedt/pholyglot","owner":"olleharstedt","description":"Experiment with PHP+C polyglot snippets and a transpiler written in OCaml","archived":false,"fork":false,"pushed_at":"2024-04-19T10:10:14.000Z","size":1312,"stargazers_count":21,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-05-01T15:45:45.274Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"OCaml","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/olleharstedt.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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}},"created_at":"2022-08-10T20:34:42.000Z","updated_at":"2024-04-19T14:29:40.000Z","dependencies_parsed_at":"2024-04-05T20:29:36.224Z","dependency_job_id":"35659194-c877-478b-8fb2-f5433063895d","html_url":"https://github.com/olleharstedt/pholyglot","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/olleharstedt%2Fpholyglot","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/olleharstedt%2Fpholyglot/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/olleharstedt%2Fpholyglot/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/olleharstedt%2Fpholyglot/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/olleharstedt","download_url":"https://codeload.github.com/olleharstedt/pholyglot/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":219861836,"owners_count":16555980,"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-10-11T09:22:11.652Z","updated_at":"2026-04-09T14:47:13.696Z","avatar_url":"https://github.com/olleharstedt.png","language":"OCaml","funding_links":[],"categories":["Uncategorized"],"sub_categories":["Uncategorized"],"readme":"## Blog posts\n\nFor information about the project, see the following blog posts:\n\n* http://olleharstedt.github.io/programming/2022/12/12/nbody-benchmark-in-php-c-polyglot-pholyglot.html\n* http://olleharstedt.github.io/programming/2022/06/11/pholyglot-php-to-php-c-transpiler.html\n\n## IRC\n\n    ,cc int $a = 10; printf(\"%d\", $a); // Now I'm curious\n    http://www.iso-9899.info/n1570.html#J.5.2p1 [Specialized identifiers] Characters other than the underscore _, letters, and digits, that are not part of the basic source\n                     character set (such as the dollar sign $, or characters in national character sets) may appear in an identifier (6.4.2).\n    ,cc int \\u0024a = 10; // this, on the other hand, is strictly conforming\n    \"Before C++11, C++ didn't allow Unicode escapes with a code point under 0xA0, except for 0x24 ($), 0x40 (@), and 0x60 (`).\"\n    \"C still has the same restrictions as C++98.\"\n\n    -w #include \u003cdlfcn.h\u003e void callbyname(const char *name, const char *arg) { ((int (*)(const char *)) dlsym(RTLD_DEFAULT, name))(arg); } callbyname(\"puts\", \"YOLO\");\n\n    olle: for your c target, probably easiest to say: int *temp = \u0026a-\u003eb; *temp = 1\n    iow the code generator treats lvalues uniformly as pointers\n\n## Notes\n\n    #define \\u0024 $\n    #define \\u003C ?\n    #define \\u002F /\n\n    #define EMPTY\n    EMPTY#ifdef\n\n    GOTO here;\n    here:\n\n    gcc polyglot.c -I/usr/include/mysql -I/usr/include/mysql/mysql -lmysqlclient\n\n    #include \"fcgi_stdio.h\"\n\n    $a = parse_int($_GET['used_id']);\n\n    // TODO: This won't deal with different mem strategies\n    // BUT: Use _Generic to switch on it? Use same new() with different behaviour - alloca, malloc, Boehm etc.\n    // BUT: Should be able to use one class with many diff allocation strats, /** @alloc stack */ etc\n    // NB: new (foo()) is valid in PHP when foo() returns string/class name\n    #define new(x) x ## __constructor(alloca(sizeof(struct x)))\n\n`g_printf` vs `printf`?\n\n`g_slice` instead of malloc?\n\nhttps://stackoverflow.com/questions/25144963/converting-a-mysql-result-into-a-json-string-in-c/25523088#25523088\n\nhttps://stackoverflow.com/questions/5451913/how-to-retrieve-form-post-data-via-cgi-bin-program-written-in-c\n\n* Class can be struct.\n    * Pass around `$__self` explicitly\n* String can use smart strings from PHP core or GNU libc\n* Array can use same array.\n** `#define array const Body*` but also need to pass length as an argument\n* Hashtable gonna be hard. Must use class for that?\n* GC with Boehm, no long-running scripts? Or glib slice/manual ref count\n    * libgc-dev on Ubuntu\n* Need docblock for arrays - no collection class built-in in PHP\n    * Fixed-size array (as in C); can't change size (at least not without copy mem; but mess up pointers?)\n    * Vector, as array but can change size\n    * (Double-linked) list\n    * Hashtable\n\n**Impossible**\n\n* Nested assoc arrays\n* Read/write from PHP session\n* Array init in both C and PHP\n** BUT: Maybe possible with `#__C__` and sed? Or just sed?\n* UTF8\n\n**Snippets**\n\nTo include PHP which is ignored in C:\n\n```php\n// ?\u003e \u003c?php $arr = $arr[0]; ?\u003e\n```\n\n## Use-cases\n\n* Read database config from JSON file\n* Read/write from/to database\n* Curl call somewhere\n* Some mathematical thing (n-body problem?)\n* Export something big to CSV\n* IRC-bot\n\n## Open questions\n\n* String concat\n* Local type inference\n* Assoc array is banned, hash table with string keys is ok\n\n## Benchmark\n\nstrip binary first\n\n    $ time f r a in {1..100}; do php polyglot.php; done\n    $ time for a in {1..100}; do ./polyglot; done\n\n    C version:\n    real    0m0,532s\n    user    0m0,266s\n    sys     0m0,217s\n\n    PHP version:\n    real    0m2,211s\n    user    0m1,338s\n    sys     0m0,810s\n\n## Transpiler\n\n* What type information is needed?\n\n```php\nfunction add(int $x, int $t): int {\n    return $x + $y;\n}\n\nfunction add_points(Point $p1, Point $p2) {\n    return new Point($x-\u003ex\n}\n```\n\nsetVariableAndTokenMappingsForExpressionManager\n\n## Notes\n\nTopic is language design and type-inference.\n\nPHP has a weird \"array\" type that's basically a hash table that also acts as a list. When transpiling PHP to a more machine-close language, one optimization is to infer the \"array\" type to a more precise type, e.g. to an actual C-like array, linked list, dynamic vector, or a traditional hash table.\n\nSome example code:\n\n```php\n$arr = [1, 2, 3];     // Fixed size integer array\n/** @var int[42] */\n$arr = [];            // Fixed-size int array with size 42\n$arr[] = 4;           // Pushing a new element on top of $arr, so either dynamic vector or linked list\n$arr[\"moo\"] = 5;      // Now it's a hash table\n$tuple = [1, \"moo\"];  // This could be a tuple, since it has different types\n```\n\nThe challenge is to deal with these different alternatives when only limited information is available.\n\n```php\nfunction foo(array $arr): string {\n    return $arr[0];  // Assume a string array, that is, string[]\n}\n\nfunction foo(int $a, string $b): array {\n    return [$a, $b];  // Tuple with size 2\n}\n```\n\nPossible to enforce stack alloc for class? So can't combine with heap alloc classes.\n\n```php\n/** @pholyglot stack-only */\nclass Point\n{\n}\n```\n\nArrays\n\n```php\nuse _ as tuple;\n\n/**\n * @param array\u003cint\u003e  // Fixed or dynamic or list?\n */\nfunction foo(array $arr): void {\n}\n\n$foo = /** @alloc stack */ new Foo();\n$foo = /** @alloc pool */ new Foo();\n\n/**\n * @param-pholly vect\u003cint\u003e\n * @return-pholly tuple\n */\nfunction bar(array \u0026$a): array\n{\n    $b = $a[0];  // Fixed-size\n    return [$b];\n}\n```\n\n```\n/**\n * OK in Psalm\n * @return \u0026array\u003cint\u003e\n */\nfunction \u0026foo(): array\n{\n    $a = [1, 2, 3];\n    return $a;\n}\n[$a, $b, $c] = \u0026foo();\necho $a;\necho $b;\necho $c;\n```\n\nError. All good if error message is empty (as in Go).\n\n```\nfunction bar(): array\n{\n    //return [128, null];\n    return [127, \"error message\"];\n}\nif (([$result, $error] = bar()) === [$result, null]) {\n    echo 'all good, we got result ' . $result;\n} else {\n    echo $error;\n}\n```\n\n## gcc\n\n    __cleanup__\n\n    http://echorand.me/site/notes/articles/c_cleanup/cleanup_attribute_c.html\n\nStack alloc vs value types:\n\n    /**\n     * @return Point@stack\n     */\n    function makePoint() {\n        $p = /** @alloc stack */ new Point();\n        ...\n        return $p;           // Break;\n        return new Point();  // Works with value types...?\n    }\n\n    /**\n     * @param SplDoublyLinkedList $points\n     */\n    function addPointToList(SplDoublyLinkedList $points) {\n        // Pool, Boehm, ref count\n        $points-\u003epush(new Point());\n    }\n\n    // Can be done with both pool, boehm, ref count?\n    // Polymorph on alloc kind?\n    array_slice($arr, 0, 1);\n\nPool:\n\n    $foos = /** @mem pool */ [new Foo(), new Foo(), new Foo()];\n    $slice = array_slice($foos, 0, 1);\n    return $slice;  // Pool can't escape, same as stack?\n\nArena is not the same as a pool.\n\nMake long == double == uintptr_t same size\n\n    21:46 size_t a = sizeof(long), b = sizeof(double), c = sizeof(uintptr_t); if (a == b \u0026\u0026 b == c) {}\n    22:01 static_assert(sizeof(long) == sizeof(double) == sizeof(uintptr_t));\n\n    $ gcc -E -dM - \u003c /dev/null | egrep 'UINTPTR_TYPE|SIZEOF_LONG'\n    #define __SIZEOF_LONG__ 8\n    #define __SIZEOF_LONG_DOUBLE__ 16\n    #define __UINTPTR_TYPE__ long unsigned int\n    #define __SIZEOF_LONG_LONG__ 8\n    $ x86_64-w64-mingw32-gcc -E -dM - \u003c /dev/null | egrep 'UINTPTR_TYPE|SIZEOF_LONG'\n    #define __SIZEOF_LONG__ 4\n    #define __SIZEOF_LONG_DOUBLE__ 16\n    #define __UINTPTR_TYPE__ long long unsigned int\n    #define __SIZEOF_LONG_LONG__ 8\n\n    x86_64 tolerates unaligned access but it's slow. Other platforms normally just segfaults.\n    alignof(max_align_t) is the safest alignment that works for all types.\n\nSingle owner as a solution to malloc from mysqli and other external libs?\n\nPHP FFI could be used?\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Folleharstedt%2Fpholyglot","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Folleharstedt%2Fpholyglot","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Folleharstedt%2Fpholyglot/lists"}