Pomimo faktu, że PHP 5 dobiegło końca, wciąż istnieją starsze aplikacje, które muszą działać w środowiskach produkcyjnych lub testowych. Jeśli instalujesz pakiety PHP za pośrednictwem repozytorium systemu operacyjnego, nadal istnieje szansa, że otrzymasz pakiety PHP 5, np. System operacyjny CentOS 7. To powiedziawszy, zawsze istnieje sposób, aby Twoje starsze aplikacje działały z nowszymi wersjami baz danych, a tym samym korzystały z nowych funkcji.
W tym poście na blogu pokażemy, jak uruchomić aplikacje PHP 5 z najnowszą wersją MySQL 8.0 w systemie operacyjnym CentOS 7. Ten blog jest oparty na rzeczywistych doświadczeniach z wewnętrznym projektem, który wymagał, aby aplikacja PHP 5 działała razem z naszym nowym MySQL 8.0 w nowym środowisku. Pamiętaj, że najlepiej byłoby uruchomić najnowszą wersję PHP 7 razem z MySQL 8.0, aby skorzystać ze wszystkich znaczących ulepszeń wprowadzonych w nowszych wersjach.
PHP i MySQL w CentOS 7
Przede wszystkim zobaczmy, jakie pliki są dostarczane przez pakiet php-mysql:
$ cat /etc/redhat-release
CentOS Linux release 7.6.1810 (Core)
$ repoquery -q -l --plugins php-mysql
/etc/php.d/mysql.ini
/etc/php.d/mysqli.ini
/etc/php.d/pdo_mysql.ini
/usr/lib64/php/modules/mysql.so
/usr/lib64/php/modules/mysqli.so
/usr/lib64/php/modules/pdo_mysql.so
Domyślnie, jeśli zainstalowaliśmy standardowe komponenty stosu LAMP, dostarczane są z CentOS 7, na przykład:
$ yum install -y httpd php php-mysql php-gd php-curl mod_ssl
Otrzymasz następujące powiązane pakiety:
$ rpm -qa | egrep 'php-mysql|mysql|maria'
php-mysql-5.4.16-46.el7.x86_64
mariadb-5.5.60-1.el7_5.x86_64
mariadb-libs-5.5.60-1.el7_5.x86_64
mariadb-server-5.5.60-1.el7_5.x86_64
Następujące moduły związane z MySQL zostaną następnie załadowane do PHP:
$ php -m | grep mysql
mysql
mysqli
pdo_mysql
Patrząc na wersję API zgłaszaną przez phpinfo() dla klientów związanych z MySQL, wszystkie są dopasowane do zainstalowanej przez nas wersji MariaDB:
$ php -i | egrep -i 'client.*version'
Client API version => 5.5.60-MariaDB
Client API library version => 5.5.60-MariaDB
Client API header version => 5.5.60-MariaDB
Client API version => 5.5.60-MariaDB
W tym momencie możemy stwierdzić, że zainstalowany moduł php-mysql jest zbudowany i zgodny z MariaDB 5.5.60.
Instalacja MySQL 8.0
Jednak w tym projekcie musimy działać na MySQL 8.0, więc wybraliśmy Percona Server 8.0, aby zastąpić domyślną istniejącą instalację MariaDB, którą mamy na tym serwerze. Aby to zrobić, musimy zainstalować repozytorium Percona i włączyć repozytorium Percona Server 8.0:
$ yum install https://repo.percona.com/yum/percona-release-latest.noarch.rpm
$ percona-release setup ps80
$ yum install percona-server-server
Jednak po uruchomieniu ostatniego polecenia otrzymaliśmy następujący błąd:
--> Finished Dependency Resolution
Error: Package: 1:mariadb-5.5.60-1.el7_5.x86_64 (@base)
Requires: mariadb-libs(x86-64) = 1:5.5.60-1.el7_5
Removing: 1:mariadb-libs-5.5.60-1.el7_5.x86_64 (@anaconda)
mariadb-libs(x86-64) = 1:5.5.60-1.el7_5
Obsoleted By: percona-server-shared-compat-8.0.15-6.1.el7.x86_64 (ps-80-release-x86_64)
Not found
Error: Package: 1:mariadb-server-5.5.60-1.el7_5.x86_64 (@base)
Requires: mariadb-libs(x86-64) = 1:5.5.60-1.el7_5
Removing: 1:mariadb-libs-5.5.60-1.el7_5.x86_64 (@anaconda)
mariadb-libs(x86-64) = 1:5.5.60-1.el7_5
Obsoleted By: percona-server-shared-compat-8.0.15-6.1.el7.x86_64 (ps-80-release-x86_64)
Not found
You could try using --skip-broken to work around the problem
You could try running: rpm -Va --nofiles --nodigest
Powyższe oznacza po prostu, że współdzielony pakiet kompatybilności Percona Server będzie przestarzały mariadb-libs-5.5.60, który jest wymagany przez już zainstalowane pakiety mariadb-server. Ponieważ jest to zwykły nowy serwer, usunięcie istniejących pakietów MariaDB nie jest dużym problemem. Usuńmy je najpierw, a następnie spróbujmy ponownie zainstalować Percona Server 8.0:
$ yum remove mariadb mariadb-libs
...
Resolving Dependencies
--> Running transaction check
---> Package mariadb-libs.x86_64 1:5.5.60-1.el7_5 will be erased
--> Processing Dependency: libmysqlclient.so.18()(64bit) for package: perl-DBD-MySQL-4.023-6.el7.x86_64
--> Processing Dependency: libmysqlclient.so.18()(64bit) for package: 2:postfix-2.10.1-7.el7.x86_64
--> Processing Dependency: libmysqlclient.so.18()(64bit) for package: php-mysql-5.4.16-46.el7.x86_64
--> Processing Dependency: libmysqlclient.so.18(libmysqlclient_18)(64bit) for package: perl-DBD-MySQL-4.023-6.el7.x86_64
--> Processing Dependency: libmysqlclient.so.18(libmysqlclient_18)(64bit) for package: 2:postfix-2.10.1-7.el7.x86_64
--> Processing Dependency: libmysqlclient.so.18(libmysqlclient_18)(64bit) for package: php-mysql-5.4.16-46.el7.x86_64
--> Processing Dependency: mariadb-libs(x86-64) = 1:5.5.60-1.el7_5 for package: 1:mariadb-5.5.60-1.el7_5.x86_64
---> Package mariadb-server.x86_64 1:5.5.60-1.el7_5 will be erased
--> Running transaction check
---> Package mariadb.x86_64 1:5.5.60-1.el7_5 will be erased
---> Package perl-DBD-MySQL.x86_64 0:4.023-6.el7 will be erased
---> Package php-mysql.x86_64 0:5.4.16-46.el7 will be erased
---> Package postfix.x86_64 2:2.10.1-7.el7 will be erased
Usunięcie mariadb-libs usunie również inne pakiety, które zależą od tego z systemu. Naszą główną troską są pakiety php-mysql, które zostaną usunięte z powodu zależności od libmysqlclient.so.18 dostarczanego przez mariadb-libs. Naprawimy to później.
Po tym powinniśmy być w stanie zainstalować Percona Server 8.0 bez błędu:
$ yum install percona-server-server
W tym momencie oto pakiety związane z MySQL, które mamy na serwerze:
$ rpm -qa | egrep 'php-mysql|mysql|maria|percona'
percona-server-client-8.0.15-6.1.el7.x86_64
percona-server-shared-8.0.15-6.1.el7.x86_64
percona-server-server-8.0.15-6.1.el7.x86_64
percona-release-1.0-11.noarch
percona-server-shared-compat-8.0.15-6.1.el7.x86_64
Zauważ, że nie mamy pakietów php-mysql, które dostarczają modułów do połączenia naszej aplikacji PHP ze świeżo zainstalowanym serwerem Percona Server 8.0. Możemy to potwierdzić, sprawdzając załadowany moduł PHP. Powinieneś otrzymać puste dane wyjściowe za pomocą następującego polecenia:
$ php -m | grep mysql
Zainstalujmy go ponownie:
$ yum install php-mysql
$ systemctl restart httpd
Teraz je mamy i są ładowane do PHP:
$ php -m | grep mysql
mysql
mysqli
pdo_mysql
Możemy to również potwierdzić, patrząc na informacje PHP w wierszu poleceń:
$ php -i | egrep -i 'client.*version'
Client API version => 5.6.28-76.1
Client API library version => 5.6.28-76.1
Client API header version => 5.5.60-MariaDB
Client API version => 5.6.28-76.1
Zwróć uwagę na różnicę między wersją biblioteki Client API a wersją nagłówka API. Efekty tego zobaczymy później podczas testu.
Zacznijmy nasz serwer MySQL 8.0, aby przetestować naszą aplikację PHP5. Ponieważ MariaDB używała datadir w /var/lib/mysql, musimy go najpierw wyczyścić, ponownie zainicjować datadir, przypisać odpowiednie prawo własności i uruchomić go:
$ rm -Rf /var/lib/mysql
$ mysqld --initialize
$ chown -Rf mysql:mysql /var/lib/mysql
$ systemctl start mysql
Pobierz tymczasowe hasło roota MySQL wygenerowane przez Percona Server z pliku dziennika błędów MySQL:
$ grep root /var/log/mysqld.log
2019-07-22T06:54:39.250241Z 5 [Note] [MY-010454] [Server] A temporary password is generated for [email protected]: 1wAXsGrISh-D
Użyj go do zalogowania się podczas pierwszego logowania użytkownika [email protected] Musimy zmienić tymczasowe hasło na coś innego, zanim będziemy mogli wykonać jakiekolwiek dalsze działania na serwerze:
$ mysql -uroot -p
mysql> ALTER USER [email protected] IDENTIFIED BY 'myP455w0rD##';
Następnie przejdź do tworzenia zasobów bazy danych wymaganych przez naszą aplikację:
mysql> CREATE SCHEMA testdb;
mysql> CREATE USER [email protected] IDENTIFIED BY 'password';
mysql> GRANT ALL PRIVILEGES ON testdb.* TO [email protected];
Po zakończeniu zaimportuj istniejące dane z kopii zapasowej do bazy danych lub utwórz obiekty bazy danych ręcznie. Nasza baza danych jest teraz gotowa do użycia przez naszą aplikację.
Błędy i ostrzeżenia
W naszej aplikacji mieliśmy prosty plik testowy, aby upewnić się, że aplikacja jest w stanie łączyć się przez gniazdo lub innymi słowy, localhost na porcie 3306, aby wyeliminować wszystkie połączenia z bazą danych przez sieć. Natychmiast otrzymamy ostrzeżenie o niezgodności wersji:
$ php -e test_mysql.php
PHP Warning: mysqli::mysqli(): Headers and client library minor version mismatch. Headers:50560 Library:50628 in /root/test_mysql.php on line 9
W tym samym czasie możesz również napotkać błąd uwierzytelniania z modułem php-mysql:
$ php -e test_mysql.php
PHP Warning: mysqli::mysqli(): (HY000/2059): Authentication plugin 'caching_sha2_password' cannot be loaded: /usr/lib64/mysql/plugin/caching_sha2_password.so: cannot open shared object file: No such file or directory in /root/test_mysql.php on line 9
Lub, jeśli pracujesz z natywną biblioteką sterowników MySQL (php-mysqlnd), otrzymasz następujący błąd:
$ php -e test_mysql.php
PHP Warning: mysqli::mysqli(): The server requested authentication method unknown to the client [caching_sha2_password] in /root/test_mysql.php on line 9
Ponadto pojawiłby się jeszcze inny problem dotyczący zestawu znaków:
PHP Warning: mysqli::mysqli(): Server sent charset (255) unknown to the client. Please, report to the developers in /root/test_mysql.php on line 9
Rozwiązania i obejścia
Wtyczka uwierzytelniania
Ani biblioteka php-mysqlnd, ani php-mysql dla PHP5 nie obsługuje nowej metody uwierzytelniania dla MySQL 8.0. Począwszy od MySQL 8.0.4 metoda uwierzytelniania została zmieniona na „caching_sha2_password”, która oferuje bezpieczniejsze haszowanie hasła w porównaniu z „mysql_native_password”, które było domyślne w poprzednich wersjach.
Aby umożliwić wsteczną kompatybilność naszego MySQL 8.0. Wewnątrz pliku konfiguracyjnego MySQL dodaj następujący wiersz w sekcji [mysqld]:
default-authentication-plugin=mysql_native_password
Zrestartuj serwer MySQL i powinieneś być dobry. Jeśli użytkownik bazy danych został utworzony przed powyższymi zmianami, np. poprzez tworzenie kopii zapasowej i przywracanie, utwórz go ponownie za pomocą instrukcji DROP USER i CREATE USER. MySQL będzie podążał za nową domyślną wtyczką uwierzytelniającą podczas tworzenia nowego użytkownika.
Niezgodność wersji podrzędnej
W przypadku pakietu php-mysql, jeśli sprawdzimy zainstalowaną wersję biblioteki, zauważymy różnicę:
$ php -i | egrep -i 'client.*version'
Client API version => 5.6.28-76.1
Client API library version => 5.6.28-76.1
Client API header version => 5.5.60-MariaDB
Client API version => 5.6.28-76.1
Biblioteka PHP jest skompilowana przy użyciu MariaDB 5.5.60 libmysqlclient, podczas gdy wersja API klienta jest w wersji 5.6.28, dostarczanej przez pakiet percona-server-shared-compat. Pomimo ostrzeżenia nadal możesz uzyskać poprawną odpowiedź z serwera.
Aby pominąć to ostrzeżenie w przypadku niezgodności wersji biblioteki, użyj pakietu php-mysqlnd, który nie zależy od biblioteki MySQL Client Server (libmysqlclient). Jest to zalecany sposób, zgodnie z dokumentacją MySQL.
Aby zastąpić bibliotekę php-mysql php-mysqlnd, po prostu uruchom:
$ yum remove php-mysql
$ yum install php-mysqlnd
$ systemctl restart httpd
Jeśli zastąpienie php-mysql nie jest opcją, ostatecznością jest ręczne skompilowanie PHP z biblioteką MySQL 8.0 Client Server (libmysqlclient) i skopiowanie skompilowanych plików bibliotek do katalogu /usr/lib64/php/modules/, zastępując stary mysqli. więc mysql.so i pdo_mysql.so. Jest to trochę kłopotliwe z małą szansą na sukces, głównie z powodu przestarzałych zależności plików nagłówkowych w obecnej wersji MySQL. Aby to obejść, wymagana jest znajomość programowania.
Niezgodny zestaw znaków
Począwszy od MySQL 8.0.1, MySQL zmienił domyślny zestaw znaków z latin1 na utf8mb4. Zestaw znaków utf8mb4 jest przydatny, ponieważ w dzisiejszych czasach baza danych musi przechowywać nie tylko znaki językowe, ale także symbole, nowo wprowadzone emoji i tak dalej. Zestaw znaków utf8mb4 to kodowanie UTF-8 zestawu znaków Unicode przy użyciu od jednego do czterech bajtów na znak, w porównaniu ze standardowym utf8 (czyli utf8mb3), który używa od jednego do trzech bajtów na znak.
Wiele starszych aplikacji nie zostało zbudowanych na zestawie znaków utf8mb4. Byłoby więc dobrze, gdybyśmy zmienili ustawienie znaków dla serwera MySQL na coś zrozumiałego dla naszego starszego sterownika PHP. Dodaj następujące dwie linie do konfiguracji MySQL w sekcji [mysqld]:
collation-server = utf8_unicode_ci
character-set-server = utf8
Opcjonalnie możesz również dodać następujące wiersze do pliku konfiguracyjnego MySQL, aby usprawnić dostęp wszystkich klientów do utf8:
[client]
default-character-set=utf8
[mysql]
default-character-set=utf8
Nie zapomnij zrestartować serwera MySQL, aby zmiany zaczęły obowiązywać. W tym momencie nasza aplikacja powinna współpracować z MySQL 8.0.
Na razie to wszystko. Podziel się z nami swoją opinią w sekcji komentarzy, jeśli masz jakiekolwiek inne problemy z przeniesieniem starszych aplikacji do MySQL 8.0.