Skip to content

Commit b46a073

Browse files
committed
Initial commit
0 parents  commit b46a073

16 files changed

+3728
-0
lines changed

.editorconfig

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
root = true
2+
3+
[*]
4+
end_of_line = lf
5+
charset = utf-8
6+
trim_trailing_whitespace = true
7+
insert_final_newline = true
8+
max_line_length = 100
9+
indent_style = space
10+
indent_size = 4
11+
12+
[*.md]
13+
max_line_length = 80
14+
15+
[Makefile]
16+
indent_style = tab

.gitattributes

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/tests export-ignore
2+
/.gitattributes export-ignore
3+
/.gitignore export-ignore
4+
/phpunit.xml export-ignore
5+
/infection.json.dist export-ignore
6+
/phpcs.xml export-ignore
7+
/phpstan.neon export-ignore
8+
/.editorconfig export-ignore
9+
/Makefile export-ignore

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
vendor/
2+
tests/reports/
3+
tests/lib/testlib.so
4+
.phpunit.result.cache

LICENSE.md

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# MIT License
2+
3+
Copyright 2020 Otto Rask
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6+
7+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8+
9+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

README.md

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# rask/libload
2+
3+
A small library to help in loading FFI libraries in a straight forward manner.
4+
5+
## Rationale
6+
7+
The vanilla methods of loading dynamic libraries via PHP FFI are simple and understandable:
8+
9+
- `\FFI::cdef()` allows you to take in a library, and write the library header definition on the fly.
10+
- `\FFI::load()` allows you to take in a library header, inside which resides `FFI_LIB` that points to a dynamic library to load.
11+
12+
This is all fine and well, apart from one thing:
13+
14+
You're locked tight into the logic of `dlopen(3)`, which is a C function to load dynamic libraries. In essence this means the following:
15+
16+
1. You cannot use relative paths for `FFI_LIB`, as those operate on current working directory, which can be whatever
17+
2. You cannot use absolute paths, as all software is runnable anywhere on any system in any path
18+
3. You cannot rely on `LD_LIBRARY_PATH` as it cannot be altered at runtime, and you would require users of your code to set it up properly
19+
4. You cannot use `/lib` or `/usr/lib`, as that would be senseless pollution on the user's system, not to speak of requiring admin privileges
20+
21+
In smaller projects with limited audiences these limitations might not matter. Or maybe the library you intend to use is a well-known and often preinstalled one (e.g. `libc`). But once you want to distribute public code that relies on a custom built FFI library, you're in trouble.
22+
23+
So, the only real reason this library exists is to bypass these limitations, and allow you to load a dynamic library in a few different ways.
24+
25+
## Features
26+
27+
- Load libraries the `dlopen(3)` way
28+
- Load libraries according to certain ways of `dlopen(3)` (e.g. allow `LD_LIBRARY_PATH` but disallow others)
29+
- Disable loading libraries using the `dlopen(3)` logic
30+
- Load libraries from specific paths, e.g. relative to the current PHP file or similar.
31+
32+
## Example
33+
34+
```php
35+
<?php
36+
37+
use rask\Libload\Loader;
38+
39+
$loader = new Loader();
40+
41+
$loader->disableDlopenLogic();
42+
$loader->addLibraryPath('/var/app/path/to/libs');
43+
44+
try {
45+
$ffi = $loader->load(__DIR__ . '/libs/my_lib.h');
46+
} catch (\rask\Libload\LibloadException $e) {
47+
// log it or something
48+
}
49+
50+
assert($ffi instanceof \FFI);
51+
```
52+
53+
Where `my_lib.h` contains
54+
55+
```h
56+
#define FFI_LIB "my_lib.so"
57+
58+
... definitions here ...
59+
```
60+
61+
The example above instantiates a new `Loader`, and on that loader we disable the default logic of PHP and `dlopen(3)`. Then we pass in a library path (directory) where the loader should look for dynamic libraries in. Our header file `my_lib.h` contains an `FFI_LIB` definition that is not of the path type, and it will be used as a relative path once we disable the `dlopen(3)` default logic.
62+
63+
So, if a dynamic library exists in `/var/app/path/to/libs/my_lib.so` it should get loaded and return a regular PHP `\FFI` instance for us.
64+
65+
## How it works
66+
67+
In a nutshell:
68+
69+
The loader reads your header file, parses the `FFI_LIB` definitions, and then just uses `\FFI::cdef()` with the header file and a proper path to the library you actually want to load.
70+
71+
A little hacky, yes.
72+
73+
## Installation
74+
75+
$ composer require rask/libload
76+
77+
## Usage
78+
79+
> to be written
80+
81+
## Todo
82+
83+
- Make this package useless, by pestering PHP core devs to add this functionality to the PHP core FFI implementation
84+
85+
## Notes
86+
87+
Currently FFI library loading in opcache preloading contexts is limited to a `php.ini` setting called `ffi.preload`, meaning all bindings you want to have available during preloading must be defined there. This package does not change that, and currently this package targets CLI projects mainly.
88+
89+
## Contributing
90+
91+
Development requirements apart from a PHP CLI installation that supports FFI, you need to have `gcc` and `make` available, and being able to compile on a system that produces Linux shared object binaries (`*.so`). This means you probably cannot run tests on Windows or Mac right now.
92+
93+
We need `gcc` and `make` so we can build a shared library for testing purposes when running PHPUnit.
94+
95+
Before sending code as a pull request:
96+
97+
- Try to add tests for your changes (`composer test`), or ask for help from the maintainers with it
98+
- Run linting and sttatic analysis against your code before commiting (`composer lint` and `composer stan`)
99+
100+
If you have problems using the package, you can raise issues. Documentation and cleanup contributions very welcome. Feature requests are OK as long as they're not too far from the core purpose of this package: making loading FFI libraries a little simpler/easier/saner/etc.
101+
102+
If you have any questions about how to contribute, you can create an issue and ask for pointers.
103+
104+
## License
105+
106+
MIT License, see [LICENSE.md](./LICENSE.md).

composer.json

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
{
2+
"name": "rask/libload",
3+
"type": "library",
4+
"description": "A small FFI library loader tool",
5+
"license": "MIT",
6+
"authors": [
7+
{
8+
"name": "Otto Rask",
9+
"email": "[email protected]"
10+
}
11+
],
12+
13+
"autoload": {
14+
"psr-4": {
15+
"rask\\Libload\\": "src/"
16+
}
17+
},
18+
19+
"autoload-dev": {
20+
"psr-4": {
21+
"rask\\Libload\\Tests\\": "tests/"
22+
}
23+
},
24+
25+
"require": {
26+
"php": ">=7.4",
27+
"roave/security-advisories": "dev-master"
28+
},
29+
30+
"require-dev": {
31+
"ext-xdebug": "*",
32+
"roave/security-advisories": "dev-master",
33+
"phpunit/phpunit": "^8.0",
34+
"phpstan/phpstan": "^0.12",
35+
"phpstan/phpstan-strict-rules": "^0.12",
36+
"ergebnis/phpstan-rules": "^0.14",
37+
"infection/infection": "^0.13",
38+
"squizlabs/php_codesniffer": "^3.5",
39+
"slevomat/coding-standard": "^5.0",
40+
"brainmaestro/composer-git-hooks": "^2.8",
41+
"rask/coding-standard": "^0.1"
42+
},
43+
44+
"scripts": {
45+
"post-install-cmd": "cghooks add --ignore-lock",
46+
"post-update-cmd": "cghooks update",
47+
"test": ["make -C ./tests/lib build", "./vendor/bin/phpunit"],
48+
"stan": "./vendor/bin/phpstan analyze -l max ./src",
49+
"lint": ["./vendor/bin/phpcs ."],
50+
"fix": "./vendor/bin/phpcbf .",
51+
"infection": "./vendor/bin/infection",
52+
"clean": "make -C ./tests/lib clean"
53+
},
54+
55+
"extra": {
56+
"hooks": {
57+
"pre-commit": [
58+
"composer lint"
59+
]
60+
}
61+
}
62+
}

0 commit comments

Comments
 (0)