PHP: syntax error, unexpected $end

Dla zdezorientowanych, w przypadku pojawienia się błędu o podobnej treści:

Parse error: syntax error, unexpected $end in /usr/home/test/application/views/v_main.php on line 1731

Najlepiej jest nie panikować.
Wiemy, że kod jest poprawny, wiemy, że błąd ten generowany jest gdy mamy niepełny kod i brakuje klamer zamykających.

Rozwiązanie:
edytujemy na serwerze plik php.ini i ustawiamy zmienną:

short_open_tag = On

i już.

amavisd nie startuje - błąd z portem 10024

Jakiś czas temu miałem przyjemność borykać się z dziwnym problemem pakietu AMaViS’a komunikującego się z Postfix’em na portach 10024 (nasłuchiwanie AMaViS’a) i 10025 (wysyłanie odpowiedzi do Postfix’a).

Przy uruchamianiu procesu amavisd pojawia się błąd:

fox# /usr/local/etc/rc.d/amavisd start
ERROR: MISSING REQUIRED BASIC MODULES:
IO::Wrap
IO::Stringy
Unix::Syslog
Compress::Zlib
MIME::Entity
MIME::Parser
Net::Server
Net::Server::PreFork
BEGIN failed-compilation aborted at /usr/local/sbin/amavisd line 232.

Dziwnym trafem (to zapewne sprawka upgrade’owania innych portów) brakuje kilku modułów Perl’a do prawidłowego funkcjonowania pakietu AMaViS.

By rozwiązać ten problem należy wejść do konsoli Perl’a poleceniem:

perl -MCPAN -e shell

i w CPAN’ie (konsola Perl’a) instalujemy brakujące moduły (z listy powyżej) zgodnie z poleceniem

install [nazwa modułu], np. install Net::Server

Po ponownym skompilowaniu brakujących modułów (startując amavisa można zweryfikować jakich jeszcze brakuje) sprawdzamy czy nasz proces działa:

fox# /usr/local/etc/rc.d/amavisd start
Starting amavisd.
Problem in Amavis::DKIM code: Can’t locate Crypt/OpenSSL/RSA.pm in @INC (@INC contains: /usr/local/lib/perl5/5.8.9/BSDPAN /usr/local/lib/perl5/site_perl/5.8.9/mach /usr/local/lib/perl5/site_perl/5.8.9 /usr/local/lib/perl5/5.8.9/mach /usr/local/lib/perl5/5.8.9) at (eval 99) line 25.
BEGIN failed-compilation aborted at (eval 99) line 25.

Wydawać się mogło, że ponowna instalacja brakujących modułów rozwiąże problem, a tu jednak zonk.

Wobec powyższego wynika, że wsiąkł również specjalny moduł odpowiedzialny za realizację tzw. DomainKeys Identified Mail (DKIM).
Doinstalowujemy więc port

cd /usr/ports/
make search name=mail-DKIM
(…)
cd /usr/ports/mail/p5-Mail-DKIM
make install clean

I ponownie sprawdzamy czy amavisd działa

fox# /usr/local/etc/rc.d/amavisd start
Starting amavisd.
Problem in antispam SA code: Can’t locate Mail/SpamAssassin.pm in @INC (@INC contains: /usr/local/lib/perl5/5.8.9/BSDPAN /usr/local/lib/perl5/site_perl/5.8.9/mach /usr/local/lib/perl5/site_perl/5.8.9 /usr/local/lib/perl5/5.8.9/mach /usr/local/lib/perl5/5.8.9) at (eval 118) line 63.
BEGIN failed-compilation aborted at (eval 118) line 63.

Teraz łapiemy się za głowę, bowiem okazuje się, że brakuje SA (SpamAssasin’a).

Wchodzimy ponownie do konsoli Perl’a i instalujemy SA

perl -MCPAN -e shell
install Mail:SpamAssassin

Po instalacji SA uruchamiamy proces amavisd kolejny raz aby przekonać się czy jest już wszystko OK.

fox# /usr/local/etc/rc.d/amavisd start
Starting amavisd.

i sprawdzamy czy widać proces

fox# ps aux | grep amavisd
vscan 83335 3.1 3.5 79112 72304 ?? Ss 10:02AM 0:08.15 amavisd (master) (perl)
vscan 83351 0.7 3.6 82116 75068 ?? I 10:03AM 0:01.22 amavisd (ch1-avail) (perl)
vscan 83352 0.0 3.5 79744 72304 ?? I 10:03AM 0:00.03 amavisd (virgin child) (perl)
root 83359 0.0 0.0 1632 844 p0 RL+ 10:03AM 0:00.00 grep amavisd

fox# /usr/local/etc/rc.d/amavisd status
amavisd is running as pid 83335 83351 83352.

Hurra, jest sukces! Udało się! Ale… czy na pewno?
Proces działa, ale poczta nie zostaje dostarczona.

Patrzymy na logi Postfix’a

tail -f /var/log/maillog

i starając się wysłać dowolnego maila widzimy w logach następujące błędy

Oct 13 05:36:16 fox amavis[25159]: (25159-14) (!!)TROUBLE in check_mail: delivery-notification FAILED: temporarily unable to send DSN to : 450 4.4.1 Can’t connect to INET4 socket 127.0.0.1: Connection refused, MTA([127.0.0.1]:10025), id=25159-14 at /usr/local/sbin/amavisd line 11386.
Oct 13 05:36:16 fox amavis[25159]: (25159-14) (!)PRESERVING EVIDENCE in /var/amavis/tmp/amavis-20091013T030230-25159
Oct 13 05:36:16 fox postfix/smtp[29075]: 5FF9B6D42B: to=, relay=127.0.0.1[127.0.0.1]:10024, delay=1689, delays=1683/0.01/0.01/5.9, dsn=4.5.0, status=deferred (host 127.0.0.1[127.0.0.1] said: 451 4.5.0 Error in processing, id=25159-14, delivery-notification FAILED: temporarily unable to send DSN to : 450 4.4.1 Can’t connect to INET4 socket 127.0.0.1: Connection refused, MTA([127.0.0.1]:10025), id=25159-14 at /usr/local/sbin/amavisd line 11386. (in reply to end of DATA command))

Oct 13 06:05:14 fox amavis[28132]: (28132-10) (!)FWD via SMTP: -> , 450 4.4.1 Can’t connect to INET4 socket 127.0.0.1: Connection refused, MTA([127.0.0.1]:10025), id=28132-10
Oct 13 06:05:14 fox amavis[28132]: (28132-10) Blocked MTA-BLOCKED, -> , Message-ID: <[email protected]>, mail_id: i8vzB+cdas+V, Hits: -2.235, size: 7058466, 127353 ms
Oct 13 06:05:15 fox postfix/smtp[29514]: 2B90C6D425: to=, orig_to=, relay=127.0.0.1[127.0.0.1]:10024, delay=21915, delays=21778/9.7/0.02/127, dsn=4.4.1, status=deferred (host 127.0.0.1[127.0.0.1] said: 450 4.4.1 id=28132-10 - Temporary MTA failure on relaying, Can’t connect to INET4 socket 127.0.0.1: Connection refused, MTA([127.0.0.1]:10025), id=28132-10 (in reply to end of DATA command))

Z powyższych błędów wynika, że jest jakiś problem przy połączeniu na porcie 10024.
Tutaj przyznam się bez bicia, że w configu Postfix’a zmieniłem wcześniej w celach testowych port z 10024 na 10025.

Aby nie mieszać, ustawienia plików konfiguracyjnych powinny być następujące

amavisd.conf

# Port na którym będzie nasłuchiwał AMaViS:
$inet_socket_port = 10024;
# Dopuszczaj połączenia jedynie poprzez lokalny adres IP:
@inet_acl = qw( 127.0.0.1 );

main.cf

# AMaViS
content_filter=smtp-amavis:[127.0.0.1]:10024

master.cf

smtp-amavis unix - - n - 2 smtp
-o smtp_data_done_timeout=1200
-o smtp_send_xforward_command=yes
-o disable_dns_lookups=yes

127.0.0.1:10025 inet n - n - - smtpd
-o content_filter=
-o local_recipient_maps=
-o relay_recipient_maps=
-o smtpd_restriction_classes=
-o smtpd_client_restrictions=
-o smtpd_helo_restrictions=
-o smtpd_sender_restrictions=
-o smtpd_recipient_restrictions=permit_mynetworks,reject
-o mynetworks=127.0.0.0/8
-o strict_rfc821_envelopes=yes
-o smtpd_error_sleep_time=0
-o smtpd_soft_error_limit=1001
-o smtpd_hard_error_limit=1000

Po doprowadzeniu konfiguracji pakietów Postfix + AMaViS do porządku, wszystko wróciło do normy

Oct 13 06:45:44 fox postfix/cleanup[83006]: 7DDC36D41B: message-id=<000d01ca6824$a61bc6b0$6400a8c0@morozov>
Oct 13 06:45:44 fox postfix/qmgr[38715]: 7DDC36D41B: from=, size=10257, nrcpt=1 (queue active)
Oct 13 06:45:44 fox amavis[80899]: (80899-04) (!!)WARN: all primary virus scanners failed, considering backups
Oct 13 06:45:49 fox postfix/smtpd[83003]: disconnect from unknown[113.169.91.43]
Oct 13 06:45:50 fox amavis[80899]: (80899-04) Blocked SPAM, [113.169.91.43] [113.169.91.43] -> , quarantine: spam-gAJtMJQua0NH.gz, Message-ID: <000d01ca6824$a61bc6b0$6400a8c0@morozov>, mail_id: gAJtMJQua0NH, Hits: 14.836, size: 10257, 11290 ms
Oct 13 06:45:50 fox postfix/smtp[83007]: 7DDC36D41B: to=, relay=127.0.0.1[127.0.0.1]:10024, delay=13, delays=1.6/0/0.01/11, dsn=2.5.0, status=sent (250 2.5.0 Ok, id=80899-04, DISCARD(bounce.suppressed))
Oct 13 06:45:50 fox postfix/qmgr[38715]: 7DDC36D41B: removed
Oct 13 06:45:55 fox postfix/anvil[83004]: statistics: max connection rate 1/60s for (smtp:113.23.24.227) at Oct 13 06:45:44
Oct 13 06:45:55 fox postfix/anvil[83004]: statistics: max connection count 1 for (smtp:113.23.24.227) at Oct 13 06:45:44
Oct 13 06:45:51 fox postfix/anvil[83004]: statistics: max cache size 1 at Oct 13 06:45:44

Po takim długim leczeniu choroby nasuwa się myśl: uwaga na upgrade portów 🙂
3mam kciuki. Pozdrawiam.

mailto z button’a

Dzisiaj coś dla developerów HTML/JavaScript i języków webowych (np. JSP/ASP/PHP).
Może dla niektórych jest to prosta rzecz, aczkolwiek kilka dłuższych chwil zajęło mi rozwiązanie tego problemu, a mianowicie jak z button’a w HTMLu można utworzyć maila w stylu takim jak link mailto

a href=”mailto:[email protected]?subject=hello world&body=hello world again!” name=”link”

Sprawa okazuje się bardzo prosta i można to zrobić następująco

INPUT TYPE=”button” VALUE=”wyślij maila” onClick=”parent.location=’mailto:[email protected]?subject=to jest tytuł maila&body=a to jest treść maila'”>

Wyjaśniając powyższy kod, należy w metodzie onClick użyć parent.location i przypisać temu to co chcieliśmy wywołać w href’ie.

Zend Optimizer - Fatal error: Incompatible file format

Po aktualizacji Zend Guarda do nowszej wersji np. 5.0.1 i po przekompilowaniu projektu może pojawić się błąd o następującej treści:

Error: Fatal error: Incompatible file format: The encoded file has format major ID 3, whereas the Optimizer expects 2 in [nazwa_pliku] on line 0

Jednak nie każdy problem taki straszny na jakiego wygląda…

Wystarczy zaktualizować ZendOptimizer’a do wersji 3.3 i Zend Engine do wersji 2.2 i będzie po kłopocie.
Funkcja phpinfo() pomoże sprawdzić załadowaną wersję Zend Optimizer’a.

Skąd ściągnąć aktualizacje powyższych?
Sprawdź w Google.
Powodzenia.

Upgrade WordPress’a

Postanowiłem wykonać przesiadkę z wersji 2.0.6 na 2.6.2 (duży, śmiały krok).

W pierwszej koleności ściągnąłem najnowszy pakiet WP ze strony autorów.
Następnie wykonałem kopie zapasowe plików poprzedniej wersji i bazy danych.
W zasadzie po wrzuceniu nowych plików do odpowiedniego katalogu WP, na stronie panelu admina WP pojawia się okienko do aktualizacji systemu (skrypt upgrade.php). Czyli wszystko jakby zgodnie z planem.
Po wykonaniu tej czynności przestała działać tylko jedna rzecz - kategorie.
Trochę czasu zajęło mi znalezienie przyczyny braku kategorii. Nie można było ani dodawać ani usuwać ani tym bardziej przydzielać wpisów.

Co się tak właściwie stało?

Otóż okazało się, że brakuje tabel z przedrostkiem wp_term:

wp_term_relationships
wp_term_taxonomy
wp_terms

Chciałem jednak, aby wszystko poszło z automatu, więc aby zachować wszystkie wpisy i strony (tego akurat upgrade nie ruszył i bardzo dobrze), musiałem wyedytować skrypt install.php (http://twoja-domena/wp-admin/install.php) i zakomentować jedną linijkę uruchamiającą funkcje sprawdzające czy WordPress jest już zainstalowany.

// Let’s check to make sure WP isn’t already installed.
if ( is_blog_installed() ) {display_header(); die(‚<h1>'.__('Already Installed').'

'.__('You appear to have already installed WordPress. To reinstall please clear your old database tables first.').'

');}

Oczywiście uruchomił się instalator WordPress.
Poszło bez przeszkód w ułamku sekundy z zachowaniem starych tabel i danych (sprawdziłem).
Warto porównać jeszcze czy atrybuty pozostałych tabel są zgodne z tymi z instalatora, ale raczej powinno być wszystko tak jak trzeba.
Warto też odkomentować spowrotem powyższy kod.
Teraz już można się cieszyć funkcjonalnością Kategorii w wersji 2.6.2 WordPressa.

Ale nie ma róży bez kolców.

Nowy WP posiada nowe struktury kategorii i przypisanych do nich wpisów.
Niestety nie wpadłem jeszcze na inny pomysł jak tylko ponowne utworzenie hierarchii kategorii i przypisania wpisów do nich. Tym razem wszystko się zapisze do nowych brakujących wcześniej tabel.
Stara tabela o nazwie wp_categories jest w tym momencie historyczna.

Openwebmail i „Premature end of script headers”

Aby nasze systemy i aplikacje działały prawidłowo lubimy je aktualizować do najnowszych wersji. Aczkolwiek są różne ‚szkoły’: jedni aktualizują a drudzy podziwiają zalety ostatnich zainstalowanych wersji systemów serwerowych nietykając aktualizacji.

Należę do tej pierwszej grupy i zachęcam jednak do korzystania z wszelkich dostępnych aktualizacji istniejących w sieci.
Ale oczywiście wszystko z głową.

Ostatnio postanowiłem zaktualizować Perla do wersji 5.8.8 i dzięki temu przestał działać Openwebmail.
W logach webowego serwera apache widziałem tylko komunikaty typu:

Premature end of script headers: openwebmail.pl

Prawa dostępu do katalogu były ustawione prawidłowo, alias skryptowy CGI (ScriptAlias) również.
Pojawiła się więc zagadka.

Szukając odpowiedzi w sieci natrafiłem na ciekawą informację dotyczącą wykonywania skryptów CGI na serwerze apache:

The most common cause of this problem is the script dying before sending the complete set of headers, or possibly any at all, to the server. To see if this is the case, try running the script standalone from an interactive session, rather than as a script under the server. If you get error messages, this is almost certainly the cause of the „premature end of script headers” message. Even if the CGI runs fine from the command line, remember that the environment and permissions may be different when running under the web server. The CGI can only access resources allowed for the User and Group specified in your Apache configuration. In addition, the environment will not be the same as the one provided on the command line, but it can be adjusted using the directives provided by mod_env.

Źródło: http://httpd.apache.org/docs/1.3/misc/FAQ.html#premature-script-headers.

Chodzi o to, że wykonywane skrypty CGI na serwerze muszą mieć dostęp do otoczenia (environment), które jest przez te skrypty zmieniane, a co za tym idzie musi być załadowany moduł mod_env.
Brakujący moduł doinstalowałem z portu php5-extensions-1.1. Jednak sama instalacja modułu nie pomogła rozwiązać problem.

Okazuje się, że w momencie wykonywania skryptów Perl korzysta z takiej opcji jak SUIDPERL (Set User ID PERL), tj. przydziela identyfikator użytkownika na czas wykonania skryptu.

Można się o tym dowiedzieć czytając informacje o zmianach w nowej wersji Openwebmaila.
Najistotniejsza informacja dla nas:

(…) Perl automatically runs in suid mode if the suid bit is set on the running script AND perl is compiled with the DOSUID option. The openwebmail-*.pl scripts still require suid, so please make sure they are properly chmod’d 4755 as usual (…)

Źródło: http://openwebmail.org/openwebmail/doc/changes.txt.

Lecz mało kto wie, że w wersji 5.8.8 Perla opcja SUIDPERL standardowo jest wyłączona.
Musimy przeinstalować Perla (z portów) z opcją SUIDPERL w następujący sposób:

cd /usr/ports/lang/perl5.8
make deinstall
make ENABLE_SUIDPERL=YES install clean

Gdy krypty perlowe Openwebmaila nadal generują te same błędy, należy jeszcze przeinstalować Openwebmaila.

Powodzenia!

Jak oswoić ZendOptimizer i ionCube?

Miałem wczoraj możliwość ustawienia ciekawego rozwiązania hashowania skryptów PHP - ionCube oraz ZendOptimizer .
Istotą sprawy było to, że wszystko miałem zainstalowane i dobrze ustawione w pliku php.ini z taką małą różnicą, że phpinfo(); jednak nie pokazywało odpowiednich informacji o tych ustawieniach, co - de facto - również nie działało.

Cóż więc jest nie tak?

Przejrzałem google w wzdłuż i wszerz i niestety nie znalazłem odpowiedzi na to pytanie. Znalazłem natomiast mnóstwo opisów dotyczących prawidłowej konfiguracji tzw. loaderów w pliku ustawień samego deskryptora PHP.

Przejrzałem jeszcze raz konfiguracje i manuale. Okazało się, że PHP nie może być skompilowane z opcją

-enable-debug

wpp. loadery nie ładują się.

Wobec tego ponownie skompilowałem PHP 5.2.0 na freeBSD 6.1 przy czym tym razem nie zaznaczałem opcji debug. Oczywiście należało również pwtórnie skompilować wszystkie używane moduły, gdyż te wcześniejsze były jakby przygotowane dla wspomnianego parametru debug.

Wynik tej akcji zobaczyłem w phpinfo():

This program makes use of the Zend Scripting Language Engine:
Zend Engine v2.2.0, Copyright (c) 1998-2006 Zend Technologies
with the ionCube PHP Loader v3.1.24, Copyright (c) 2002-2006, by ionCube Ltd., and
with Zend Optimizer v3.2.2, Copyright (c) 1998-2006, by Zend Technologies
with Zend Extension Manager v1.2.0, Copyright (c) 2003-2006, by Zend Technologies

SugarCRM i problem DateTime w PHP 5.2.0

Chcemy być „Up To Date”. Robimy aktualizację paczek na serwerach linux/unix. Okazuje się, iż najnowsza wersja PHP 5.2.0 posiada zmienioną nieco obsługę klasy DateTime, co idzie w parze pojawiającym się błędem np. w aplikacji Open Source Sugar CRM.

Fatal error: Cannot redeclare class DateTime in /home/www/sugarcrm/modules/Calendar/DateTime.php on line 29

Na szczęście na forum SugarCRM pojawiła się łatka o nazwie os_datetimefix.zip, umożliwiająca prawidłową obsługę tej klasy i jej podobnych wykorzystywanych w aplikacji.
Wystarczy ją rozpakować i odpowiednie pliki nadpisać w podkatalogach katalogu /modules/.
Nie zapomnijcie o kopii zapasowej plików oryginalnych!

Link do forum: http://www.sugarcrm.com/forums/showthread.php?t=16477&highlight=php+5.2.0+RC4+error

Powodzenia!

Kody Apache’a

Ostatnio miałem problem ze prawidłową obsługa ErrorDocument, tj. zapisaniem w logu serwera httpd wpisu o kodzie 404.
Gdy mamy np. błędną stronę typu /index.php?page=NieMaTakiejStrony i wiemy, że faktycznie takiej strony nie ma, to samo ustawienie w httpd.conf:

ErrorDocument 404 /index.php?page=404

powodowało, że wyświetlała mi się przygotowana wcześniej w CMSie strona z treścią informacyjną o nieznalezieniu strony, natomiast w logach apache’a widziałem wpis typu:

/index.php?page=404 200 OK

Problem polegał na tym, że widząc informację o błędzie, w logu serwera www zapisywała się informacja, że nie był to błąd.
Musiałem zastosować małą sztuczkę. W httpd.conf ustawiłem:

ErrorDocument 404 /404.php

Czyli plik inny aniżeli jeden spośród stron dynamicznych ładowanych z contentu. Jego źródło wygląda następująco (tylko 2 linijki):

header(„HTTP/1.0 404 Not Found”);
echo file_get_contents(„/index.php?page=404”);

Czyli najpierw wysyłam do serwera www nagłówek z kodem 404 Not Found, a następnie wyświetlam treść strony, do której poprzednio prowadziła użytownika aplikacja CMS.
Dzięki tej sztuczce w logach apache’a mamy wpis

/index.php?page=NieMaTakiejStrony 404 -

Czyli użytkownik widzi informację o nieznalezieniu strony, a w logach apache’a zapisuje się info o błędzie strony.

Po takim tricku jesteśmy szczęśliwi.

Poniżej zamieszczam listę kodów błędów - napewno komuś się przyda.

Successful Client Requests
200 - OK
201 - Created
202 - Accepted
203 - Non-Authorative Information
204 - No Content
205 - Reset Content
206 - Partial Content

Client Request Redirected
300 - Multiple Choices
301 - Moved Permanently
302 - Moved Temporarily
303 - See Other
304 - Not Modified
305 - Use Proxy

Client Request Errors
400 - Bad Request
401 - Authorization Required
402 - Payment Required (not used yet)
403 - Forbidden
404 - Not Found
405 - Method Not Allowed
406 - Not Acceptable (encoding)
407 - Proxy Authentication Required
408 - Request Timed Out
409 - Conflicting Request
410 - Gone
411 - Content Length Required
412 - Precondition Failed
413 - Request Entity Too Long
414 - Request URI Too Long
415 - Unsupported Media Type

Server Errors
500 - Internal Server Error
501 - Not Implemented
502 - Bad Gateway
503 - Service Unavailable
504 - Gateway Timeout
505 - HTTP Version Not Supported

Czasami się przydaje.

Linki permanentne w WordPress

Całe dwa dni zajęło mi szukanie błędu nieprawidłowego działania funkcji linków permanentnych, jaką oferuje najnowszy WordPress 2.0.6.

Aplikacja automatycznie tworzy plik .htaccess w katalogu, w którym została zainstalowana.
Oto treść tego pliku:

# BEGIN WordPress

RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]

W ustawieniach ogólnych Odnośników wybrałem Oparty na dacie i nazwie publikacji.
Sprawdzam…. nie działa, co robić?

Zarzuciłem google.pl 🙂

Szukam i szukam, szukam i szukam i tak naprawdę zapomniałem, że samo włączenie RewriteEngine On nic nie daje.

Mam ustawionego Apache’a na domenach wirtualnych, gdzie każda domena ma swój odrębny katalog oraz swój odrębny httpd.conf.

Okazało się, że nie ustawiłem jednej istotnej rzeczy…

Order allow,deny
Allow from all
AllowOverride All

Eh… człowiek uczy się całe życie… 🙂 Powodzenia!