Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ To your `php.ini`.
require __DIR__ . '/vendor/autoload.php';
use Jcupitt\Vips;

// handy for Windows
Vips\FFI::addLibraryPath("C:/vips-dev-8.16/bin");

// check libvips version
echo 'libvips version: ' . Vips\Config::version() . PHP_EOL;

Expand Down
73 changes: 48 additions & 25 deletions src/FFI.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,15 @@ class FFI
*/
private static bool $ffi_inited = false;

/**
* A list of paths where libvips might reside.
*
* @internal
*/
private static array $libraryPaths = [
"" // system library
];

/**
* Look up these once.
*
Expand Down Expand Up @@ -169,6 +178,30 @@ public static function atLeast(int $x, int $y, int $z = 0): bool
self::$library_micro >= $z);
}

/**
* Adds a directory to the search path for shared libraries.
*
* This method has no effect if FFI handles are already initialized or
* if the specified path is already included.
*
* @param string $path The path of the library.
* @return bool `true` if the path was added; otherwise, `false`.
*/
public static function addLibraryPath(string $path): bool
{
// Already initialized.
if (self::$ffi_inited) {
return false;
}

if (!in_array($path, self::$libraryPaths)) {
self::$libraryPaths[] = $path;
return true;
}

return false;
}

/**
* Shut down libvips. Call this just before process exit.
*
Expand Down Expand Up @@ -208,14 +241,16 @@ private static function libraryName(string $name, int $abi): string
}

private static function libraryLoad(
array $libraryPaths,
string $libraryName,
string $interface
): ?\FFI {
Utils::debugLog("trying to open", ["libraryName" => $libraryName]);
foreach ($libraryPaths as $path) {
foreach (self::$libraryPaths as $path) {
Utils::debugLog("trying path", ["path" => $path]);
try {
if ($path !== '') {
$path .= '/';
}
$library = \FFI::cdef($interface, $path . $libraryName);
Utils::debugLog("success", []);
return $library;
Expand Down Expand Up @@ -252,37 +287,25 @@ private static function init(): void

$is_64bits = PHP_INT_SIZE === 8;

$libraryPaths = [
"" // system library
];

$vipshome = getenv("VIPSHOME");
if ($vipshome) {
// lib<qual>/ predicates lib/
$libraryPaths[] = $vipshome . ($is_64bits ? "/lib64/" : "/lib32/");
// lib/ is always searched
$libraryPaths[] = $vipshome . "/lib/";
}

if (PHP_OS_FAMILY === "OSX" || PHP_OS_FAMILY === "Darwin") {
// Homebrew on Apple Silicon
$libraryPaths[] = "/opt/homebrew/lib/";
self::$libraryPaths[] = "/opt/homebrew/lib";
// See https://github.com/Homebrew/brew/issues/13481#issuecomment-1207203483
$libraryPaths[] = "/usr/local/lib/";
self::$libraryPaths[] = "/usr/local/lib";
}

$vips = self::libraryLoad($libraryPaths, $vips_libname, <<<'CPP'
$vips = self::libraryLoad($vips_libname, <<<'CPP'
int vips_init (const char *argv0);
const char *vips_error_buffer (void);
int vips_version(int flag);
CPP);

if ($vips === null) {
// drop the "" (system path) member
array_shift($libraryPaths);
array_shift(self::$libraryPaths);
$msg = "Unable to open library '$vips_libname'";
if (!empty($libraryPaths)) {
$msg .= " in any of ['" . implode("', '", $libraryPaths) . "']";
if (!empty(self::$libraryPaths)) {
$msg .= " in any of ['" . implode("', '", self::$libraryPaths) . "']";
}
$msg .= ". Make sure that you've installed libvips and that '$vips_libname'";
$msg .= " is on your system's library search path.";
Expand Down Expand Up @@ -777,13 +800,13 @@ private static function init(): void
* one that libvips itself is using, and they will share runtime types.
*/
self::$glib =
self::libraryLoad($libraryPaths, $vips_libname, $glib_decls) ??
self::libraryLoad($libraryPaths, $glib_libname, $glib_decls);
self::libraryLoad($vips_libname, $glib_decls) ??
self::libraryLoad($glib_libname, $glib_decls);
self::$gobject =
self::libraryLoad($libraryPaths, $vips_libname, $gobject_decls) ??
self::libraryLoad($libraryPaths, $gobject_libname, $gobject_decls);
self::libraryLoad($vips_libname, $gobject_decls) ??
self::libraryLoad($gobject_libname, $gobject_decls);

self::$vips = self::libraryLoad($libraryPaths, $vips_libname, $vips_decls);
self::$vips = self::libraryLoad($vips_libname, $vips_decls);

# Useful for debugging
# self::$vips->vips_leak_set(1);
Expand Down