我想将 GDFONTPATH 环境变量从 Apache vhost.conf 传递到 PHP。我该怎么做?
我正在运行:Debian 12、Apache 2.4、PHP 8.2
imagettftext
),并且想要使用系统范围内可能不可用的特定字体。这可以通过设置环境变量 GDFONTPATH
来完成,PHP 函数 putenv
非常适合此目的。
但是,
putenv
也是一个安全问题,因此在我的生产服务器上,我将其列在我的disable_functions
中的php.ini
上,因此,我无法在我的PHP脚本中设置此环境变量。相反,在我的 Apache vhost .conf 文件中使用 SetEnv
来设置它似乎是一个好主意(这是一个简化的示例):
<Directory "/srv/www/example.com/public">
AllowOverride None
Require all granted
SetEnv GDFONTPATH "/srv/www/example.com/private/fonts"
</Directory>
问题是,这并不像[我]预期的那样工作!它确实设置了GDFONTPATH,但不是在本地,所以我得到以下结果:
getenv('GDFONTPATH'); # Returns '/srv/www/example.com/private/fonts'
getenv('GDFONTPATH', true); # Returns ''
libgd 函数忽略这一点;他们只查看 local 环境变量,我似乎只能使用
putenv
函数设置该变量。但出于安全原因,在我的生产服务器上启用 putenv
并不实际,因此我真的想在我的 Apache 配置中使用 SetEnv
!
FWIW:我编写了一个“实用”的解决方法,它将使用 PHP 的
getenv
从 Apache 的 SetEnv
读取路径,然后将完全限定的字体文件名传递给 gdlib。这可行,但不太理想!我可能会发布此代码以供将来参考,但我真的想要一个可以从 Apache 设置环境的解决方案。 这篇文章可能提供一个潜在的解决方案,但看起来它适用于所有虚拟主机,所以同样,这并不理想。
我对环境变量和“本地”环境变量之间的区别有点粗略。
如果无法通过 Apache 配置传递 GDFONTPATH,那么这是我正在使用的解决方案。
注意:这是基于此处的示例代码。
<?php
// Functions very loosely based on Pythons os.path
// https://docs.python.org/3/library/os.path.html
function pathJoin(string $path, string ...$paths) {
foreach ($paths as $segment) {
if ($segment[0] === DIRECTORY_SEPARATOR) {
$path = $segment;
}
else {
if ($path[-1] !== DIRECTORY_SEPARATOR) {
$path .= DIRECTORY_SEPARATOR;
}
$path .= $segment;
}
}
return $path;
}
function pathSplit(string $path) {
$pos = strrpos($path, DIRECTORY_SEPARATOR);
if ($pos === false) {
return ['', $path];
}
return [substr($path, 0, $pos + 1), substr($path, $pos + 1)];
}
function pathBasename(string $path) {
return pathSplit($path)[1];
}
// Set the content-type
header('Content-Type: image/png');
// Create the image
$im = imagecreatetruecolor(400, 30);
// Create some colors
$white = imagecolorallocate($im, 255, 255, 255);
$grey = imagecolorallocate($im, 128, 128, 128);
$black = imagecolorallocate($im, 0, 0, 0);
imagefilledrectangle($im, 0, 0, 399, 29, $white);
// The text to draw
$text = 'Testing...';
$font = 'arial;msttcorefonts/Arial;dejavu/DejaVuSans';
// ^ this is a font-stack! So list of fonts to _try_
// Use Apache SetEnv directive to set GDFONTPATH. Since libgd will
// only use the "local" GDFONTPATH, this won't work directly, and
// the following code is required to build an absolute file-path:
if (getenv('GDFONTPATH', true) === false) {
$gdFontPath = getenv('GDFONTPATH');
if ($gdFontPath) {
$delim = ';';
$tok = strtok($font, $delim);
while ($tok !== false) {
$fontFile = pathJoin($gdFontPath, $tok);
if (strrpos(pathBasename($fontFile), '.') === false) {
$fontFile .= '.ttf';
}
if (file_exists($fontFile)) {
$font = $fontFile;
break;
}
$tok = strtok($delim);
}
}
}
// Add some shadow to the text
imagettftext($im, 20, 0, 11, 21, $grey, $font, $text);
// Add the text
imagettftext($im, 20, 0, 10, 20, $black, $font, $text);
// Using imagepng() results in clearer text compared with imagejpeg()
imagepng($im);
imagedestroy($im);
与 php.net 示例代码的差异:
$font = 'arial.ttf';
但在 Debian 上不起作用,我怀疑只在 Windows 上有效。但是,如果该示例使用字体堆栈,它可能会列出多个字体/相对路径,这就是我在示例中所做的。$font
。否则,它会保持原样$font
。这几乎满足了我的要求,但如果 SetEnv 按我的预期工作,则不需要这些额外的代码!
注意:我只测试
.ttf
文件扩展名,而 libgd 代码 尝试 .ttf
、.pfa
、.pfb
和 .dfont
扩展名。
正如您已经发现的,
SetEnv GDFONTPATH "..."
设置Apache HTTPD内部变量,可通过 PHP 的 SAPI 通过 getenv() 访问,但不能通过 getenv(..., true) 访问系统环境变量。简单地说,PHP getenv()s' $local = true 变量不是 Apache 内部环境变量。
GDFONTPATH 系统环境变量被gdlib用作字体搜索路径参数,与PATH类似,但用于字体文件,而不是可执行文件。
这是有道理的,例如要访问系统配置的字体,这类似于在该系统上执行二进制文件:仅指定基本名称就足够了。
但是,当您打算使用该系统上未配置的字体文件时,再次与二进制文件类似,您需要提供解析为该字体的类型文件的目录条目的字体的绝对路径名。
$font_dirname = '/srv/www/example.com/private/fonts';
$font_basename = 'example.ttf';
$font_filename = $font_dirname . '/' . $font_basename;
set_error_handler(fn (int $type, string $message, string $file = null, int $line = null)
=> throw new ErrorException($message, 0, $type, $file, $line),
E_ALL,
);
imagettftext($image, $size, $angle, $x, $y, $color, $font_filename, $text);
restore_error_handler();
测试对象:
GDFONTPATH 参考资料: