Linux 如何强制 PHP 使用 iconv 的 libiconv 版本而不是 CentOS 安装的 glibc 版本?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4743080/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-05 02:33:10  来源:igfitidea点击:

How can I force PHP to use the libiconv version of iconv instead of the CentOS-installed glibc version?

phplinuxapacheglibciconv

提问by Randell

The code I'm working on runs perfectly on Windows XP and on Mac OS X. When testing it on CentOS (and on Fedora and Ubuntu), it's not working properly. Searching the nets led me to the conclusion that it's the glibcversion of the iconvthat's causing the problem. So now I need the libiconvversion of iconvfor Zend Lucene to work properly.

我正在处理的代码在 Windows XP 和 Mac OS X 上完美运行。在 CentOS(以及 Fedora 和 Ubuntu)上测试它时,它无法正常工作。搜索网络使我得出结论,这是导致问题的glibc版本iconv。所以现在我需要Zend Lucene的libiconv版本iconv才能正常工作。

I already downloaded libiconv and configured it with --prefix=/usr/local, make, then make installwithout any errors. It seems that it was successfully installed because executing /usr/local/bin/iconv --versionsays the version is the libiconv. Although a simple iconv --versionstill gives the glibcversion.

我已经下载了 libiconv 并使用--prefix=/usr/local,对其进行了配置make,然后make install没有任何错误。似乎它已成功安装,因为执行时/usr/local/bin/iconv --version说版本是libiconv. 虽然简单iconv --version还是给出了glibc版本。

Then I recompiled PHP from source using --with-iconv=/usr/local. But still, the phpinfo()is showing the iconvbeing used is the glibcversion. I've also already tried several other compiles using --with-iconv-diror using /usr/local/bin/php.

然后我使用--with-iconv=/usr/local. 但是,仍然phpinfo()显示iconv正在使用的是glibc版本。我也已经尝试过使用--with-iconv-dir或使用其他几种编译/usr/local/bin/php

Of course, I restarted the web server after recompiling PHP.

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

I have the following line in my /etc/httpd/conf/httpd.conf:

我在我的以下行/etc/httpd/conf/httpd.conf

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

and libphp5.sois actually in /usr/lib/httpd/modules.

并且libphp5.so实际上在/usr/lib/httpd/modules.

phpinfo()shows PHP 5.3.3. I also yum removed the pre-installed PHP 5.1.* just to make sure. But the iconv is still using the glibc version.

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

ldd /usr/lib/httpd/modules/libphp5.sogives

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)

This is a cross-post from: NullPointer.ph

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

采纳答案by linuxbuild

I just have changed my php-5.3.3 from glibc's iconv to GNU libiconv through the manual recompiling of the php iconv extension. Follow these steps:

我刚刚通过手动重新编译 php iconv 扩展将我的 php-5.3.3 从 glibc 的 iconv 更改为 GNU libiconv。按着这些次序:

  1. download php-5.3.3 source codepackage
  2. extract it and go into php-5.3.3/ext/iconvsubdirectory
  3. execute phpizecommand (if you have no such command then install php-develpackage)
  4. (*) edit configure file (vim configure): add iconv_impl_name=""at 4664 line (exact line number on your system configuration may be different):

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

    ...
    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

And now I run php -i|grep "iconv impl"and got:

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

iconv implementation => libiconv

* This trick forces configure to select the GNU libiconv instead of glibc's iconv. By default it checks for glibc's iconv at first step and does not check for GNU libiconv at all.

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

回答by Pablo Alba

I do not know about CentOS, but in debian based distributions, like Ubuntu, you can choose the version of the program that you want to lauch defining the symbolic links at /etc/alternatives.

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

So, if you change the symbolink link /etc/alternatives/iconv in order to point to /usr/local/bin/iconv, this point forward it should use the correct version.

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

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

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

回答by Pablo Alba

Are you sure that LD_LIBRARY_PATHis set properly for httpd (web server) process? If not, try setting it like:

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

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

... in the script that starts the process (i.e. apachectl).

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

The lddoutput you showed looks correct, but you invoked lddfrom user's environment, and httpd's might be different.

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

It could also help to set PATH to "/usr/local/bin:${PATH}", just in case.

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

回答by peoro

Your module (libphp5.so) is linked to two shared libraries which are providing the same symbol (in this case the symbol is iconvand the libraries are libiconv.so.2and probably libc.so.6).

您的模块 ( libphp5.so) 链接到提供相同符号的两个共享库(在这种情况下,符号是iconv,库是libiconv.so.2并且可能是libc.so.6)。

When this happen, the first loaded symbol is used: probably libc.so.6gets loaded before libiconv.so.2and thus it's the one providing you with the iconvsymbol.

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

You can force the dynamic loader to load a library before any other; you can do this by setting the LD_PRELOADenvironment variable to the library you want to preload.

你可以强制动态加载器先加载一个库;您可以通过将LD_PRELOAD环境变量设置为要预加载的库来执行此操作。

I'm not an expert about Apache, so I'm not totally sure about how it works, how it starts its process and what processes it uses, but I think that setting LD_PRELOADbefore running apache should do the trick:

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

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


A little example to show LD_PRELOADin action:

一个显示LD_PRELOAD在行动中的小例子:

Will compile myfopen.cas a shared library (myfopen.so): it will provide a fopensymbol (already defined in libc):

将编译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

Compiling printfopen.cas an executable (printfopen) which just prints the result of fopen; will link it against both libcand libmyfopen(LD_LIBRARY_PATHis needed to let the linker look for the libraries also in .):

编译printfopen.c为可执行文件 ( printfopen),它只打印fopen; 将它链接到libclibmyfopenLD_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)

Now I'm running it, to test if LD_PRELOADworks:

现在我正在运行它,以测试是否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

By default it loads libmyfopenbefore libc, then I tried to force loading libcand then libmyfopenfirst.

默认情况下它加载libmyfopenbefore libc,然后我尝试强制加载libc,然后libmyfopen首先加载。

I guess that in your case libcis getting loaded before libiconvbecause the former is loaded by the application (apache?) before it loads the PHP module.

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

回答by Nopius

I understand, that this question is already answered and almost dead, but recently I tried to find a way to compile PHP with libiconv because in PHP I couldn't convert "?" from UTF8 to CP1251 even with iconv //IGNORE. But I found another solution that worked for me without recompilation (just use //TRANSLIT):

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

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

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

//TRANSLIT will transliterate ONLYunknown characters (not all, as some may guess), so it converts Russian 'ё', but transliterates unknown '?' to 0x95 (which looks the same in the target charset).

//TRANSLIT 将音译未知字符(并非所有字符,有些人可能会猜到),因此它会转换俄语 'ё',但会音译未知字符 '?' 到 0x95(在目标字符集中看起来相同)。