How to install Ioncube Loader PHP on Linux Ubuntu

Ioncube Loader Extenion allows to run encoded PHP scripts by Ioncube Encoder.

  • Ioncube Loader Extension – extension which starts witch PHP process that can read and run encoded files. The extension is free.
  • Ioncube Encoder – software that allows encode and obfulscate PHP scripts using license key to description.

This tutorial shows how to install Ioncube Loader Extension.

1. Download Ioncube loader extension.

Go to the http://www.ioncube.com/loaders.php url and locate your proper platofrm version.

If you are not sure what platform (x86 or 64-bit, TS or NTS ) you need, just run phpinfo() and read from „System” and „PHP Extension Build”. For example this entry looks like:

System: Linux athlan-VirtualBox 3.13.0-24-generic #46-Ubuntu SMP Thu Apr 10 19:11:08 UTC 2014 x86_64

PHP Extension Build: API20121212,NTS

I am using 64-bit platform, NTS (non-thread safe).

So copy proper link and call:

wget http://downloads3.ioncube.com/loader_downloads/ioncube_loaders_lin_x86-64.tar.gz

Extract the package

tar xvfz ioncube_loaders_lin_x86-64.tar.gz

athlan@athlan-VirtualBox:~/tmp/ioncube$ ls -1
ioncube_loader_lin_4.1.so
ioncube_loader_lin_4.2.so
ioncube_loader_lin_4.3.so
ioncube_loader_lin_4.3_ts.so
ioncube_loader_lin_4.4.so
ioncube_loader_lin_4.4_ts.so
ioncube_loader_lin_5.0.so
ioncube_loader_lin_5.0_ts.so
ioncube_loader_lin_5.1.so
ioncube_loader_lin_5.1_ts.so
ioncube_loader_lin_5.2.so
ioncube_loader_lin_5.2_ts.so
ioncube_loader_lin_5.3.so
ioncube_loader_lin_5.3_ts.so
ioncube_loader_lin_5.4.so
ioncube_loader_lin_5.4_ts.so
ioncube_loader_lin_5.5.so
ioncube_loader_lin_5.5_ts.so
ioncube_loader_lin_5.6.so
ioncube_loader_lin_5.6_ts.so
LICENSE.txt
loader-wizard.php
README.txt
USER-GUIDE.txt

2. Copy extension to PHP extension dir

Locate your extenion dir:

athlan@athlan-VirtualBox:~/tmp/ioncube$ php -i | grep extension_dir
extension_dir => /usr/lib/php5/20121212 => /usr/lib/php5/20121212

Copy here your proper loader, in my case:

cp ./ioncube_loader_lin_5.5.so /usr/lib/php5/20121212

3. Add extension to php.ini file

You must add Ioncube Loader to php.ini file pointing proper file:

zend_extension=ioncube_loader_lin_5.5.so

Make sure that extension is the first loaded extension for PHP, because the error will appear:

PHP Fatal error: Unable to start ionCube Loader module in Unknown on line 0

In my Ubuntu the extensions directory are under: /etc/php5/mods-available directory – one per extension. So define ioncube.ini file. In php+apache2 for ubuntu there are configuratios groupped by environment, one is apache2, so I make symbolic link to include my .ini file:

ln -s .etc/php5/mods-available/ioncube.ini /etc/php5/apache2/conf.d/01-ioncube.ini

I named my file by prefix 01- to make sure that it will be the first included extension.

4. Check configuration

Make file with phpinfo() and check if Ioncube is loaded under „Additional Modules” and „with the ionCube PHP Loader (enabled) + Intrusion Protection from ioncube24.com (unconfigured) v5.0.11, Copyright (c) 2002-2015, by ionCube Ltd.”:

ioncube-phpinfo

ioncube-phpinfo2

Done!

Known issues:

Apache hangs while start

The apache2 instance did not start within 20 seconds. Please read the log files to discover problems

Probably you have not proper version of your extension included (TS or NTS). Please verify that comparing to your phpinfo() „System” and „PHP Extension Build”.

Invalid extension definition

[Sat Jul 11 15:44:24 2015] [warn-phpd] The ionCube Loader is a Zend-Engine extension and not a module (pid 3038)
[Sat Jul 11 15:44:24 2015] [warn-phpd] Please specify the Loader using 'zend_extension’ in php.ini (pid 3038)

You have included Ioncube by extension= while zend_extension= should be used.

Ioncube Loader is loaded after another extensions

PHP Fatal error: [ionCube Loader]
The Loader must appear as the first entry in the php.ini file in Unknown on line 0

You have to specify zend_extension directive in php.ini as a first extension loaded. To make sure, just place it as a first line.

 

Symfony2 Redis Session Handler

Context

When you scale a PHP

application you have to consider several aspects of runtime environment such us:

  • Bytecode caching (e.x. APC or Zend Optimizer Plus or eAccelerator), more;
  • Reading project files from RAM instead of HDD;
  • Caching and minify static content etc.
  • One additional aspect is storing sessions.

By default, PHP stores sessions in files. There are also several approaches to speed up saving sessions, such us memcached, mapping save_path folder as ramdisc, etc.

In scaling approaches there is important that many worker nodes (with deployed application) runs the same code, round-robin selected or load-ballanced, but have the same space to store sessions, because there is no guarantee in distributes architecture, that next user’s request will be handled by the same node. This implies, that session memory have to be shared between nodes, unfortunately storing these data in local

RAM doesn’t meet this requirement.

Redis as PHP Session Handler

One of additional approach to storing sessions in fast-memory is Redis – key-value store. This could be configured as centralized or distributed database.

There is available a Redis session_handler for PHP. To use it:

  1. install Redis first as a service [more]
  2. copy/compile redis.so PHP extension [more information]
  3. register an extension in php.ini configuration file
  4. reconfigure session.save_handler in your php.ini configuration file, or set it directly on runtime by writing for e.x.:
ini_set('session.save_handler', 'redis');
ini_set('session.save_path', 'tcp://localhost:6379');
Redis Session Handler in Symfony 2

I am using Symfony 2 framework. Unfortunately, 4th step don’t affects the application. You have to register own SessionHandler in config.yml file:

framework:
 session:
 handler_id: session_handler_redis

This configuration uses new SessionHandler registered ad session_handler_redis Symfony Service (more).

We have to write own SessionHandler in Symfony. I have found the Redis SessionHandler proposed by Andrej Hudec on GitHub (original code here). I have decided to use and improve existing implementation.

Declare new SessionHandler class somewhere in your project:

<?php

namespace Fokus\Webapp\CommonBundle\SessionHandler;

use \Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler;

/**
 * NativeRedisSessionStorage.
 *
 * Driver for the redis session 
save hadlers provided by the redis PHP extension. * * @see https://github.com/nicolasff/phpredis * * @author Andrej Hudec <pulzarraider@gmail.com> * @author Piotr Pelczar <me@athlan.pl> */ class NativeRedisSessionHandler extends NativeSessionHandler { /** * Constructor. * * @param string $savePath Path of redis server. */ public function __construct($savePath = "") { if (!extension_loaded('redis')) { throw new \RuntimeException('PHP does not have "redis" session module registered'); } if ("" === $savePath) { $savePath = ini_get('session.save_path'); } if ("" === $savePath) { $savePath = "tcp://localhost:6379"; // guess path } ini_set('session.save_handler', 'redis'); ini_set('session.save_path', $savePath); } }

Now, add the entry that declares

the class as a Symfony Service in services.yml file:

services:
 session_handler_redis:
 class: Fokus\Webapp\CommonBundle\SessionHandler\NativeRedisSessionHandler
 arguments: ["%session_handler_redis_save_path%"]

I have improved Andrzej’s code that you can configure the session handler calling it’s constructor and pass the Redis connection string just in services in Symfony, without touching ini_set or php.ini settings. As you see, the %session_handler_redis_save_path% parameter has been used.

Now, declare the value of parameter in parameters.yml file:

session_handler_redis_save_path: tcp://localhost:6379

That’s all!

Just refresh your page, use the session such us in after loging and check out if it works. Type in command line:

redis-cli

and show all keys stored by PHP Session Handler. Keys begins with string PHPREDIS_SESSION:.

KEYS PHPREDIS_SESSION*

Example output:

redis 127.0.0.1:6379> KEYS PHPREDIS_SESSION*
1) "PHPREDIS_SESSION:s4uvor0u5dcsq5ncgulqiuef14"
2) "PHPREDIS_SESSION:dcu54je80e6feo5rjqvqpv60h7"

Hope it helped!

 

ZF2 Translate in Controller

If you want to

use the translator in controller like in view, just like that:

$this->translate('Hello')

instead of ugly:

$this->getServiceLocator()->get('translator')->translate('Hello')

You have to write own controller plugin, just like view helper Zend\I18n\View\Helper\Translate.
Of course, you can invoke the plugin with the same signature:
__invoke($message, $textDomain = null, $locale = null)

To register a new plugin, put these lines in your configuration module.config.php:

'controller_plugins' => array(
    'factories' => array(
        'translate' => 'Application\Controller\Plugin\Translate',
    ),
),

Now, create your own plugin:

< ?php

namespace Application\Controller\Plugin;

use Zend\Mvc\Controller\Plugin\AbstractPlugin;

use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\ServiceManager\FactoryInterface;

use Zend\I18n\Translator\Translator;

class Translate implements FactoryInterface
{
    public function createService(ServiceLocatorInterface $serviceLocator)
    {
        $serviceLocator = $serviceLocator->getController()->getServiceLocator();
        $serviceFactory = new TranslatorServiceFactory();
        $translator = $serviceFactory->createService($serviceLocator);
        
        return new TranslatorProxy($translator);
    }
}

final class TranslatorProxy extends AbstractPlugin
{
    private $translator;
 
public function __construct(Translator $translator) { $this->translator = $translator; } public function __invoke($message, $textDomain = 'default', $locale = null) { return $this->translator->translate($message, $textDomain, $locale); } public function __call($method, $args) { return call_user_func_array([$this->translator, $method], $args); } public static function __callstatic($method, $args)
{ return call_user_func_array([$this->translator, $method], $args); } }

How it works?

You see, the ServiceLocator passed in

createService(ServiceLocatorInterface $serviceLocator)

factory in configuration space controller_plugins, does have no access to the Config service in ServiceLocator in controller. So you cannot get the configuration and create the Translate object via TranslatorServiceFactory.

Instead of that, you can access to the ServiceLocator assigned to the controller for which our helper has been invoked, by typing $serviceLocator->getController().

Of course, $serviceLocator passed in createService method is instance of Zend\Mvc\Controller\PluginManager.

Why proxy?

The object returned via plugin factory has to implement

Zend\Mvc\Controller\Plugin\PluginInterface

which is abstractly implemented in

Zend\Mvc\Controller\Plugin\AbstractPlugin

so we created proxy object to forward all calls from our plugin to the Translate object.

Hope it helped!

 

How to set ID Doctrine2 manually

I faced the

problem with preserving manually setted ID’s of records represented by entities having id

with auto generate strategy.

If for some reasons you really want to set the ID manually either

rely on generating strategy (@GeneratedValue strategy), you have to set:
@GeneratedValue(strategy="NONE")

But if your application rely on auto identifiers creation (@GeneratedValue(strategy="AUTO")) and you have to exceptionally set the ID manually (e.x. for synchronization), you have to change the strategy dynamicaly by injecting it into meta object of entity:

$metadata = $this->entityManager->getClassMetaData(get_class($entity));
$metadata->setIdGenerator(new \Doctrine\ORM\Id\AssignedGenerator());

Where $entity is enetity you want to persist and there is an working entity manager under $this->entityManager.

 

How to disable trash wordpress feature plugin

Howdy! Recently, I have been working a lot with WordPress engine, modyfying behind the site by writing plugins. I wonder to share my knowledge I collected under The Wordrpess Optimalizations tag, where a lot of simple tricks will be published. I also know that in the web there are a few of advises related with turning WordPress optimized and combination with your’s flair, the WordPress can become a powerfull, huge toll able to build simply sites as well as advanced solutions. The main advantage of WordPress is well developed backend, so you don’t have to care about beautiful and simply way to publish content and concentrate in frontent, striving to make a website much friendly to end user.

Unfortunately, the WordPress has many features that in common are not required in your project. Obviously, it generates relatively huge overhead to server, database and so one.

This post describes how to disable the „Trash” feature, which has been introduced in version 2.9 aimed to keep posts until permament deletion, such as deleting files in operating system. Deleted items are not deleted permanently from the database in fact, instead they appears in „Trash” by marking them as deleted, setting the flag.

Disabling trash manually

To disable trash in wordpress, you can simply define the constant EMPTY_TRASH_DAYS, for example in your wp-config.php.

define('EMPTY_TRASH_DAYS', 0);

From now, all options „Move to trash” will no longer apperar in admin pages, the functionality is just disabled. Just… but copies of posts marked as deleted are still in the database. To truncate them, just execute the sql statement:

DELETE p, p_rel, p_meta
 FROM wp_posts p
 LEFT JOIN wp_term_relationships p_rel ON (p.ID = p_rel.object_id)
 LEFT JOIN wp_postmeta p_meta ON (p.ID = p_meta.post_id)
 WHERE p.post_status = 'trash'

All posts marked as „Move to trash” will be deleted, and theirs post meta (custom fields) and taxonomy relations will be truncated too. The database now is clean.

Writing simple plugin.

We will write a simple plugin. In the /wp-content/plugins/MyPlugin/ create a file plugin.php, and the code within:

define('EMPTY_TRASH_DAYS', 0);

register_activation_hook(__FILE__, function() {
 global $wpdb;

$query = "DELETE p, p_rel, p_meta
 FROM " . $wpdb->posts . " p
 LEFT JOIN " . $wpdb->term_relationships . " p_rel ON (p.ID = p_rel.object_id)
 LEFT JOIN " . $wpdb->postmeta . " p_meta ON (p.ID = p_meta.post_id)
 WHERE p.post_status = 'trash'";

$wpdb->query($query);
});

The Trash feature is disabled by setting the EMPTY_TRASH_DAYS constant, and posts moved to trash will be deleted while plugin activation. We used register_activation_hook function with anonymous function javascript-like callback function (PHP 5.3).

Download the plugin

The plugin compiled to be ready to install is available here:

Hope the post will be helpfull.

 

PHP cache, semafory

Praktyka cache’owania danych jest powszechna wśród programistów aplikacji webowych ze względu na optymalizację dostępu do danych bezpośrednio ze źródła ich pochodzenia, a w szczególności:

  • trudność dostępu (np. wykonanie skomplikowanych połączeń),
  • ograniczenia dostępu (np. limit odpytywania),
  • długi czas oczekiwania na dane; powodów jest wiele.

O ile tematyką stworzenia samego mechanizmu cache zajęli się m.in. Nospor, możecie podejrzeć jak to wygląda w Zend_Cache, Symfony, czy Kohana; tak ja chciałbym zwrócić uwagę na jeszcze jedną rzecz.

Zazwyczaj schemat kodu wygląda mniej więcej tak:

< ?php
$oCache = new Cache(); // tworzony jest jakis obiekt cache

if($oCache->expired(3600) || !is_array($aData = $oCache->load())) // sprawdzamy, czy jest cache i nie wygasł
{
  $aData = $oModel->GetSomething(); // zbieramy dane z bazy danych
  $oCache->save($aData);
}

// $aData przechowuje nasze dane do użytku
?>

Symulacja, parę linijek kodu, a ile nieszczęść.

Wszystko działa pięknie, dopóki nie spotkamy się z sytuacją, gdy setki osób (procesów) jednocześnie zechcą zbierać takie dane z bazy danych. Przeprowadźmy zatem krótką dywagację. Załóżmy, że użytkownik #1 wchodzi na stronę, stwierdza, że nie ma cache, lub jest nieświeży, wówczas przechodzi do połączenia się z bazą danych i zaczyna zbierać dane. W tym samym czasie, zanim użytkownikowi #1 zostaną zwrócone dane wchodzi użytkownik #2, który stwierdza, że nie ma cache, bo użytkownik #1 jeszcze nie zebrał danych, postanawia połączyć się z bazą i zrobić to samo, co użytkownik #1, powtarzając niepotrzebnie czynność i dodatkowo obciążając bazę. Można by iść dalej i wprowadzić n użytkowników, którzy powtarzają czynność, dopóki dane nie pojawią się w cache i kolejni użytkownicy będą z niego korzystać. Co się stanie natomiast, gdy kolejka tak narośnie, że użytkownikowi #1 zabraknie zasobów systemowych, aby ukończyć proces zbierania danych, co spowoduje, że pozostałym też? Kolejka będzie wydłużała się w nieskończoność, póki system operacyjny nie podejmie żadnych działań (np. odłączy bazę danych, lub po prostu wyłączy serwer, np. w IIS7 wyłączy cały application pool). Aby doszło do tej kolizji nie jest potrzebne wcale natężenie użytkowników, serwer może akurat np. zajmować się wysyłką maili lub nieoptymalnie zrobionym procesem, który zajmuje zasoby, a w tym czasie wejdzie tylko pięciu użytkowników.

Parę linijek kodu, a ile nieszczęść.

Pojęcie semafora.

Semafor w informatyce – jest chronioną zmienną lub abstrakcyjnym typem danych, który stanowi klasyczną metodę kontroli dostępu przez wiele procesów do wspólnego zasobu w środowisku programowania równoległego.

Więcej na temat semaforów na Wikipedii, bądź w Podstawy informatyki / Stefan Węgrzyn. – Warszawa : Państwowe Wydawnictwo Naukowe, 1982.

Podejście do problemu.

< ?php
$oCache = new Cache();

if($oCache->expired(3600) || !is_array($aData = $oCache->load()))
{
  $oCache->savePrepare(); // stawiamy semafor

  $aData = $oModel->GetSomething();
  $oCache->save($aData); // metoda save() może (nie musi) od razu zwolnić semafor, gdy próba zapisu się zakończy
  // jeżeli metoda save() nie zwalnia zasobu, możemy np. użyć:
  // $oCache->saveFinalize();
}

// $aData przechowuje nasze dane do użytku
?>

Rozwiązaniem jest zastosowanie semafora blokującego dostęp do zasobu (w tym przypadku abstrakcyjnie „cache”, mniej abstrakcyjnie może być to plik na dysku, przestrzeń w pamięci operacyjnej, rekord w bazie danych, cokolwiek, co cache przerzymuje). Dla wartości semafora = 1 zasób jest wolny (nieużywany, jest 1 cache), gdy jest mniejszy/równy 0 zasób jest zajęty, ktoś z niego „korzysta”. Zajętość zasobu powinna być sprawdzana przy próbie odczytu. Dopóki zasób nie zostanie zwolniony, nie będzie można określić, czy są dane w cache. Jeżeli nie można określić, czy dane są w cache, należy zaczekać na zwolnienie zasobu.

Teraz nasze rozwiązanie nie dopuści do przytoczonej w powyższym przykładzie sytuacji. Zanim cache nie zostanie odblokowany po próbie zapisu, nie uzyskamy odczytu, czekając na niego i nie przechodząc w skrypcie nigdzie dalej.

Gdy save() się nie powiedzie? Można zastosować timeouty odczytu na load(). Wówczas złapalibyśmy wyjątek i przeszli dalej do realizacji zapisu, tak, jakby semafora nie było.

Implementacja.

Do swoich kodów podchodzę jak najbardziej abstrakcyjnie (tutaj idealnie nada się wzorzec fabryki), zatem stworzyłem klasę Cache, która obsługuje 'silniki’ implementujące interfejs Cache_Engine. Jednym z nich jest silnik Cache_Engine_File, który wykorzystuje pliki na dysku do składowania cache.

Najprostszym semaforem dla plików jest funkcja flock() (gotowe, sprawdzone rozwiązanie, w dodatku na poziomie systemu plików, nic tylko implementować). Sprawa wygląda bardzo prosto, dopóki nie zwolnimy flagi LOCK_EX po jej założeniu, ludzie nie będą czytali z pliku, czekając na zwolnienie dostępu. Ktoś powie: truizm, blokować pliki powinno się przed wykonywaniem na nich operacji. Tak. Ale grunt, w którym miejscu to zablokowanie nastąpi. Wykorzystujemy blokowanie do wyższego celu.

Wg. dokumentacji nie można polegać na flock() w przypadku Windows98 oraz systemów FAT32. Zbyt dużym poziomem abstrakcji jest dla mnie stawianie serwisu na pamięci flash lub Win98, ale faktycznie, najprostsza pamięć flash z systemem FAT32 może się czasem zdarzyć w serwerowniach i nie jest to wcale taki głupi pomysł. Co wtedy? Jako semafor możemy stworzyć plik z suffiksem .lock obok tworzonego pliku cache. Gdy plik istnieje oznacza to, że cache jest zablokowany, jeżeli nie – jest wolny. Czekamy tak długo, aż zostanie usunięty plik .lock.

Przykładowy kod źródłowy.

Przykładowy kod źródłowy obsługuje Cache_Engine_File oraz Cache_Engine_Filelock, gdzie w drugim przypadku można klasy użyć spokojnie na partycjach FAT32. Kod jest przykładowy, dlatego nie obsługuje m.in. zagnieżdżania plików w katalogach, usuwanie cache’u itd, zaimplementowałem tylko zapis i odczyt.

Klasy zostały napisane tak, aby zgłaszane przez nie błędy były zgodnie z ideologią hierarchiczną Exceptions w PHP, przy okazji zapraszam do lektury wpisu „Wyjątki w PHP” autorstwa Tomasza Jędrzejewskiego (Zyxits).

Przykładowe czekanie na zwolnienie pliku .lock:

< ?php

protected function _waitUnlock($iWaitTimeout)
{
  if($iWaitTimeout)
  {
    try
    {
      // quick first check
      if(is_file($this->_path(false, 'lock')))
      {
        // wait for unlock file
        $iWaitTimeout /= 1000000;
        $iLockTime = microtime(true);
        $bLockWait = true;

        // wait for the file
        try
        {
          while(is_file($this->_path(false, 'lock')))
          {
            $iLockWaitDelta = microtime(true) - $iLockTime;

            if($iLockWaitDelta > $iWaitTimeout && $iWaitTimeout !== true)
              { $bLockWait = false; break; }

            usleep(rand(1, 999));
          }
        }
        // cache lock path does not exists
        catch(Cache_Exception_Runtime $oE) {}

        if(!$bLockWait)
          throw new Cache_Exception_Runtime('Unable to access cache, it is totally locked, after "' . $iWaitTimeout . '" s.');
      }
    }
    // cache lock path does not exists
    catch(Cache_Exception_Runtime $oE) {}
  }
  else
  {
    try
    {
      if(is_file($this->_path(false, 'lock')))
        throw new Cache_Exception_Runtime('Unable to access cache, it is currently locked, after "' . $iWaitTimeout . '" s.');
    }
    // cache path does not exists
    catch(Cache_Exception_Runtime $oE) {}
  }
  
  return true;
}

?>
 

Podtrzymanie sesji

Dla niektórych wygasanie sesji jest zabezpieczeniem (banki, etc.). Realizując jeden z projektów oczekiwałem od systemu tego, aby użytkownik nigdy nie gubił sesji, gdy ma otwarte okno w przeglądarce. Dlaczego? Może dodaje posta, być może uzupełnia dość obszerny tekst na stronie. Gdy klika zapisz, przerzuca go do strony logowania, a cały tekst zniknął za sprawą tego, że jego przeglądarka nie zapisuje wartości pól formularza. Skąd to znamy.

Jak użytkownik gubi sesję?

  1. Jego ciastko wygasa, więc serwer nie może go zidentyfikować z sesją.
  2. Po jakimś czasie, choćby odtworzył ciastko, plik sesji znika z naszego serwera (garbage collection).

Rozwiązania:

  1. Wydłużenie czasu wygasania ciastka i sesji.
  2. Odświeżenie strony w interwale mniejszym, niż wynosi czas wygasania sesji i ciastka.

Rozmyślając nad podtrzymaniem sesji, próbowałem znaleźć wszystkie metody oraz wybrać najlepszą. Wszystkie sprowadzają się do „odświeżenia” strony lub jej fragmentu tak, aby nasz silnik wykonał tylko potrzebne session_start(); czyli podtrzymanie aktywności sesji. Jest kilka mniej lub bardziej zadowalających sposobów:

  1. Odświeżenie całej strony.
    To może spowodować, że dane wprowadzane przez użytkownika w formularzu zostaną utracone. Ponad to, jeżeli użytkownik czyta newsy, denerwującym może być fakt, że lista nagle zostanie przescrollowana do góry (prócz opery).
  2. Wysłanie requestu ajax w tle.
    Minusem jest to, że trzeba używać biblioteki ajax lub pisać dodatkowy kod javascriptu. Jeżeli ktoś na stronie używa jakiegoś ajaxa – co za różnica. Poza tym same plusy.
  3. Odświeżanie ukrytej ramki iframe lub elementu frameset.
    Minusów usablity prawie brak. Brak potrzeby instalacji javascriptów i ajaxa. Odświeżacz powinien wysłać nagłówek Refresh lub odpowiedni metatag.

Sposób 3 wydaje mi się najlepszy. Można go ulepszyć w ten sposób, aby ramka nie wysyłała żądania zaraz po załadowaniu strony. Powodowałoby to podwójne requesty do serwera.

Zapewne znajdą się osoby, które powiedzą: a co z użytkownikami, którzy mają wyłączone ramki, lub ich przeglądarki w ogóle ich nie obsługują. Zapytam wówczas: a co z użytkownikami, którzy nie akceptują cisteczek (wówczas sesje nie są dla nich użyteczne, chyba, że użyjemy przesłyki jej identyfikatora w adresie url). Dopytam również: a co z użytkownikami, którzy mają wyłączony Javascript? Patologiczne przepadki się po prostu pomija 😉

 

Hashowanie haseł z solą

Przeglądając forum.php.pl często widziałem, jak użytkownicy przechowują hasła w swoich bazach danych. Najczęściej używają funkcji hashujących md5, sha1 i sha2. Wszystko wygląda bardzo dobrze, hasła są przechowywanie bezpiecznie. No właśnie… na ile bezpiecznie.

Nie będę tutaj rozwodził się nad zabezpieczeniem baz danych, w których owa baza haseł się znajduje, ale nad samym zahashowanym ciągu. Wszyscy doskonale wiemy, że istnieją bazy md5 (sha1, sha2 również).

Przezorny zawsze ubezpieczony. Wiadomo, że nigdy nic nie wiadomo.

Pokażę, jak dodatkowo zabezpieczyć nasze hasła. Będą przechowywane w tej samej bazie danych, używając tych samych metod hashowania, a jednak szansa na „złamanie” hasła (wyszukania w bazie) będzie niemożliwa. Posłużymy się ciągiem znaków zwanym przez programistów solą (salt). Przykład implementacji możemy znaleźć w forum IPB, natomiast phpBB pozbawione jest tego fjuczuru ficzera. Cała sprawa sprowadza się do wygenerowania dowolnego kawałka ciągu znaków i doklejenia go do hasła. Sól potrzebna nam będzie również przy porównaniu hasła, więc trzeba ją zapisać w bazie danych obok hasła.

Poniżej zamieszczam przykładową klasę, która obsługuje solenie haseł. Doklejanie soli może być napisane w dowolny sposób, zależy to od Waszej wyobraźni. Ja dodatkowo dodałem element „losowy” w postaci doklejenia do soli wyniku działania funkcji microtime().

 

13 piątek, pechowo? Nie… koniec PHP4

PHP 4 end of life announcement

Takim nagłówkiem dnia 13 lipca (tj. piątek) 2007 roku na oficjalnej stronie PHP ujawnił się news dotyczący oficjalnego zamknięcia projektu PHP 4.

Today it is exactly three years ago since PHP 5 has been released. In those three years it has seen many improvements over PHP 4. PHP 5 is fast, stable & production-ready and as PHP 6 is on the way, PHP 4 will be discontinued.

The PHP development team hereby announces that support for PHP 4 will continue until the end of this year only. After 2007-12-31 there will be no more releases of PHP 4.4. We will continue to make critical security fixes available on a case-by-case basis until 2008-08-08. Please use the rest of this year to make your application suitable to run on PHP 5.

For documentation on migration for PHP 4 to PHP 5, we would like to point you to our migration guide. There is additional information available in the PHP 5.0 to PHP 5.1 and PHP 5.1 to PHP 5.2 migration guides as well.

Tłumaczenie by Paweł 'hwao’ Halicki:

Dziś (tj. 13 lipca 2007) mijają dokładnie trzy lata od daty wydania w pełni stabilnego PHP5. Przez ten okres czasu ciężko pracowaliśmy nad PHP4 czego owocem były liczne poprawki. Lecz teraz, kiedy PHP5 stało się szybkie, stabilne i w pełni dojrzałe, a prace nad PHP6 są w toku, możemy oznajmić iż projekt PHP4 zostanie zamknięty.

Deweloperzy pracujący nad PHP oświadczają, że wsparcie dla PHP4 będzie kontynuowane jedynie do końca tego roku. Po 31 grudnia 2007 nie będą ukazywać się już kolejne wersje PHP z linii 4.4. Wszystkie krytyczne błędy będą poprawiane będą jedynie do 8 sierpnia 2008 roku. Prosimy aby wszyscy programiści, wykorzystali czas pozostały do końca roku, aby ich aplikacje były kompatybilne z PHP5.

Wszelkie niezbędne wskazówki pomocne w migracji z PHP4 do PHP5 możecie znaleźć w pod linkiem migration guide. Są też dostępne wskazówki jak migrować z PHP5.0 do PHP5.1 a także PHP5.1 do PHP5.2.