【问题标题】:How can I force PHP to use the libiconv version of iconv instead of the CentOS-installed glibc version?如何强制 PHP 使用 libiconv 版本的 iconv 而不是 CentOS 安装的 glibc 版本?
【发布时间】:2011-06-12 04:48:36
【问题描述】:

我正在编写的代码可以在 Windows XP 和 Mac OS X 上完美运行。在 CentOS(以及 Fedora 和 Ubuntu)上对其进行测试时,它无法正常运行。搜索网络使我得出结论,导致问题的是iconvglibc 版本。所以现在我需要iconvlibiconv 版本才能让Zend Lucene 正常工作。

我已经下载了 libiconv 并将其配置为 --prefix=/usr/localmake,然后是 make install,没有任何错误。好像安装成功了,因为执行/usr/local/bin/iconv --version说版本是libiconv。虽然一个简单的iconv --version 仍然给出了glibc 版本。

然后我使用 --with-iconv=/usr/local 从源代码重新编译 PHP。但是,phpinfo() 显示正在使用的iconvglibc 版本。我也已经使用--with-iconv-dir/usr/local/bin/php 尝试了其他几个编译。

当然,我在重新编译PHP后重启了web服务器。

我的/etc/httpd/conf/httpd.conf 中有以下行:

LoadModule /usr/lib/httpd/modules/libphp5.so

libphp5.so实际上在/usr/lib/httpd/modules中。

phpinfo() 显示 PHP 5.3.3。我还 yum 删除了预装的 PHP 5.1.* 以确保。但是iconv仍然使用glibc版本。

ldd /usr/lib/httpd/modules/libphp5.so给了

linux-gate.so.1 =>  (0x003b1000)
/usr/local/lib/preloadable_libiconv.so (0x00110000)
libcrypt.so.1 => /lib/libcrypt.so.1 (0x001ed000)
librt.so.1 => /lib/librt.so.1 (0x0021f000)
libmysqlclient.so.15 => /usr/lib/mysql/libmysqlclient.so.15 (0x003b2000)
libldap-2.3.so.0 => /usr/lib/libldap-2.3.so.0 (0x0026e000)
liblber-2.3.so.0 => /usr/lib/liblber-2.3.so.0 (0x00370000)
libiconv.so.2 => /usr/local/lib/libiconv.so.2 (0x00516000)
libfreetype.so.6 => /usr/lib/libfreetype.so.6 (0x002a8000)
libpng12.so.0 => /usr/lib/libpng12.so.0 (0x00228000)
libz.so.1 => /usr/lib/libz.so.1 (0x00328000)
libcurl.so.3 => /usr/lib/libcurl.so.3 (0x00f23000)
libm.so.6 => /lib/libm.so.6 (0x0033b000)
libdl.so.2 => /lib/libdl.so.2 (0x00364000)
libnsl.so.1 => /lib/libnsl.so.1 (0x0037e000)
libxml2.so.2 => /usr/lib/libxml2.so.2 (0x00f5f000)
libssl.so.6 => /lib/libssl.so.6 (0x0862c000)
libcrypto.so.6 => /lib/libcrypto.so.6 (0x04145000)
libgssapi_krb5.so.2 => /usr/lib/libgssapi_krb5.so.2 (0x08e2d000)
libkrb5.so.3 => /usr/lib/libkrb5.so.3 (0x0611a000)
libk5crypto.so.3 => /usr/lib/libk5crypto.so.3 (0x005f4000)
libcom_err.so.2 => /lib/libcom_err.so.2 (0x0024e000)
libidn.so.11 => /usr/lib/libidn.so.11 (0x071f5000)
libc.so.6 => /lib/libc.so.6 (0x08aa6000)
libpthread.so.0 => /lib/libpthread.so.0 (0x00397000)
/lib/ld-linux.so.2 (0x00251000)
libresolv.so.2 => /lib/libresolv.so.2 (0x0748a000)
libsasl2.so.2 => /usr/lib/libsasl2.so.2 (0x07ddf000)
libkrb5support.so.0 => /usr/lib/libkrb5support.so.0 (0x062b7000)
libkeyutils.so.1 => /lib/libkeyutils.so.1 (0x00369000)
libselinux.so.1 => /lib/libselinux.so.1 (0x0913b000)
libsepol.so.1 => /lib/libsepol.so.1 (0x07eb4000)

这是来自NullPointer.ph的交叉帖子

【问题讨论】:

  • ldd /usr/lib/httpd/modules/libphp5.soldd /usr/libexec/httpd/httpd 显示什么? AFAIK,apache 也依赖于libiconv。您不能在一个进程中加载​​两个版本的libiconv
  • 更新问题以反映ldd /usr/lib/httpd/modules/libphp5.so。第二个命令给了我一个No such file or directory
  • 尝试使用--with-iconv=shared,/usr/local进行配置
  • @Ismail,我也试过这个,但没有用。还是谢谢。

标签: php linux apache glibc iconv


【解决方案1】:

我刚刚通过手动重新编译 php iconv 扩展将我的 php-5.3.3 从 glibc 的 iconv 更改为 GNU libiconv。请按以下步骤操作:

  1. 下载php-5.3.3 source code
  2. 解压并进入php-5.3.3/ext/iconv子目录
  3. 执行phpize命令(如果你没有这样的命令则安装php-devel包)
  4. (*) 编辑配置文件 (vim configure):在 4664 行添加iconv_impl_name=""(您的系统配置上的确切行号可能不同):

    ...
    iconv_impl_name=""
        if test -z "$iconv_impl_name"; then
          { $as_echo "$as_me:${as_lineno-$LINENO}: checking if using GNU libiconv" >&5<
    ...
    
  5. ./configure --with-iconv=/usr/local|grep iconv:

    checking if using GNU libiconv... yes
    
  6. make

  7. sudo make install

现在我运行php -i|grep "iconv impl" 并得到:

iconv implementation => libiconv

* 这个技巧强制 configure 选择 GNU libiconv 而不是 glibc 的 iconv。默认情况下,它会在第一步检查 glibc 的 iconv,而根本不检查 GNU libiconv。

【讨论】:

  • 让我想知道为什么 glibc 是 Linux 的默认实现,而 libiconv 是 Mac 和 Windows 的默认实现。
  • 请注意——不确定它是否有任何区别,但在尝试从源代码编译 PHP 5.3.8 作为主要 PHP 时,上述方法对我来说不太适用。/ configure 没有接受或遵守 ext/iconv/configure 脚本的更改。所以我继续对顶级 php-5.3.8 目录中的主 ./configure 脚本进行了上述更改,它就像一个魅力。谢谢!
【解决方案2】:

我知道,这个问题已经得到解答并且几乎已经死了,但是最近我试图找到一种使用 libiconv 编译 PHP 的方法,因为在 PHP 中我无法将“∙”从 UTF8 转换为 CP1251,即使使用 iconv //IGNORE . 但我找到了另一个无需重新编译即可为我工作的解决方案(只需使用 //TRANSLIT):

iconv("UTF8", "CP1251//TRANSLIT//IGNORE", $text)

//TRANSLIT 将音译ONLY 未知字符(不是全部,有些人可能会猜到),因此它会转换俄语的“ё”,但会将未知的“∙”音译为 0x95(在目标字符集)。

【讨论】:

  • 遗憾的是,它还没有死。
【解决方案3】:

您的模块 (libphp5.so) 链接到提供相同符号的两个共享库(在本例中,符号是 iconv,库是 libiconv.so.2,可能是 libc.so.6)。

发生这种情况时,将使用第一个加载的符号:可能 libc.so.6libiconv.so.2 之前加载,因此它是为您提供 iconv 符号的符号。

您可以强制动态加载器先加载库;您可以通过将LD_PRELOAD 环境变量设置为您要预加载的库来做到这一点。

我不是 Apache 方面的专家,所以我不完全确定它是如何工作的,它如何启动它的进程以及它使用什么进程,但我认为在运行 apache 之前设置 LD_PRELOAD 应该可以解决问题:

LD_PRELOAD=/usr/local/lib/libiconv.so.2

一个小例子来展示LD_PRELOAD的实际应用:

myfopen.c 编译为共享库(myfopen.so):它将提供fopen 符号(已在libc 中定义):

$ cat myfopen.c
int fopen(const char *path, const char *mode){ return -1; }
$ gcc -o libmyfopen.so myfopen.c -shared

printfopen.c 编译为可执行文件(printfopen),它只打印fopen 的结果;将它与libclibmyfopen 链接起来(需要LD_LIBRARY_PATH 才能让链接器也在. 中查找库):

$ cat printfopen.c
#include <stdio.h>
int main( ) {
    printf( "%d\n", fopen("","") );
    return 0;
}
$ gcc -o printfopen printfopen.c -L. -lmyfopen
$ LD_LIBRARY_PATH=. ldd printfopen
    linux-gate.so.1 =>  (0xb779d000)
    libmyfopen.so => ./libmyfopen.so (0xb779a000)
    libc.so.6 => /lib/libc.so.6 (0xb762f000)
    /lib/ld-linux.so.2 (0xb779e000)

现在我正在运行它,以测试 LD_PRELOAD 是否有效:

$ LD_LIBRARY_PATH=. ./printfopen
-1
$ LD_PRELOAD=/lib/libc.so.6 LD_LIBRARY_PATH=. ./printfopen
0
$ LD_PRELOAD=libmyfopen.so LD_LIBRARY_PATH=. ./printfopen
-1

默认情况下,它会先加载libmyfopen,然后再加载libc,然后我尝试强制加载libc,然后再加载libmyfopen

我猜在您的情况下,libclibiconv 之前被加载,因为前者是在加载 PHP 模块之前由应用程序(apache?)加载的。

【讨论】:

  • 我运行了LD_PRELOAD=/usr/local/lib/libiconv.so.2,并重新启动了 apache httpd,但 iconv 仍在使用 glibc 实现。我错过了什么?
  • @Randell:你确定加载 PHP 模块的进程已经在 LD_PRELOAD 环境变量设置正确的情况下启动了吗?尝试检查 PHP 环境变量。也许 apache 启动脚本会在启动 apache 守护进程之前重置环境?
  • @Randell:尝试使用export 命令将LD_PRELOAD 导出到环境:export LD_PRELOAD=/usr/local/lib/libiconv.so.2。然后,如果您想检查 LD_PRELOAD 是否在 PHP 环境中,您可以使用 PHP getenv function$_ENV array(不确定两者之间的区别:尝试同时使用)。
【解决方案4】:

您确定为 httpd(Web 服务器)进程正确设置了 LD_LIBRARY_PATH 吗? 如果没有,请尝试将其设置为:

export LD_LIBRARY_PATH="/usr/local/lib:${LD_LIBRARY_PATH}"

...在启动进程的脚本中(即apachectl)。

您显示的ldd 输出看起来正确,但您从用户环境调用了ldd,并且httpd 可能不同。

将 PATH 设置为“/usr/local/bin:${PATH}”也有帮助,以防万一。

【讨论】:

  • 我跑了export LD_LIBRARY_PATH="/usr/local/lib:${LD_LIBRARY_PATH}",但没有任何改变。
【解决方案5】:

我不了解 CentOS,但在基于 debian 的发行版中,例如 Ubuntu,您可以选择要在 /etc/alternatives 中定义符号链接的程序版本。

因此,如果您更改符号链接链接 /etc/alternatives/iconv 以指向 /usr/local/bin/iconv,那么向前指向它应该使用正确的版本。

http://www.debian-administration.org/articles/91

【讨论】:

  • /etc/alternatives 适用于可执行文件,而不是库。
猜你喜欢
  • 2011-05-08
  • 1970-01-01
  • 2015-03-31
  • 2017-07-30
  • 2020-02-24
  • 1970-01-01
  • 2011-02-12
  • 1970-01-01
  • 2013-02-23
相关资源
最近更新 更多