Niezalogowany [ logowanie ]
Subskrybuj kanał ATOM Kanał ATOM

Autor wpisu: Diabl0, dodany: 15.11.2008 23:19, tagi: php

Dzisiaj odgrzewany kotlet, ale nadal wiele osób korzysta z Smarty i może im się to przydać.

Wiele osób korzystających z Smarty’ego używa go tylko jako systemu szablonów do separacji kodu PHP od HTML. Dużo mniej osób zdaje sobie natomiast sprawę z jego rozbudowanego mechanizmu cache i potencjału jaki w nim drzemie.

Zapewne dla wielu osób jednym z większych braków Smarty’ego jest możliwość określenia czasu trzymania cache (cache_lifetime) dla poszczególnych plików szablonów. Normalne włączenie cache i ustawienie jakiegoś cache_lifetime sprawia że strona traci swoją dynamiczność a przy bardziej rozbudowanych projektach potrafią się dziać „cuda” gdy na przykład po zalogowaniu wciąż pokazuje nam się formularz logowania zamiast profil użytkownika. Poniżej postaram się przedstawić swój pomysł na ominięcie tego efektu, a przy okazji często znaczne przyśpieszenie działania całego serwisu i ograniczenie ilości przetwarzanych danych.

Pomysł

Pracując nad jedym z projektów głównym celem było zminimalizowanie ilości zapytań do bazy danych. Pierwszym pomysłem było pobieranie danych z bazy i trzymanie ich w PEAR::Cache do ponownego wykorzystania. Jednakże przeglądając różne fora w poszukiwaniu innych pomysłów na optymalizację zwróciłem uwagę na wbudowany w Smarty’ego mechanizm cache i zastanowiło mnie czy nie dało by się go przerobić tak aby pasował do potrzeb obecnego projektu… Przeglądając dokumentację odnośnie cache zarysował mi się pewien pomysł który postanowiłem przetestować.

Z grubsza polega on na zbudowaniu szablonu na zasadzie modułów. W PHP wykonywane są podstawowe operacje w rodzaju rozpoznania akcji, pobrania danych użytkownika i wywołania odpowiedniego szablonu. Natomiast szablony zostały podzielone na „moduły” odpowiedzialne za wyświetlenie niewielkich fragmentów serwisu w rodzaju listy kategorii czy listy ostatnich tematów na forum.

Działanie

W typowym rozwiązaniu lista tematów czy kategorii została by na początku pobrana z bazy danych i w formie tablicy przekazana do szablonu. W moim rozwiązaniu nie pobieram tych danych tylko od razu wywołuję główny szablon. Tam natomiast znajdują się tagi  {insert} Smarty’ego realizujące za mnie brudną robotę. W zamierzeniu twórców powinny służyć one do wstawiania treści która nie jest cacheowana (funkcja wykonuje się nawet w szablonie który jest w cache). Ja postanowiłem wykorzystać to do przeciwnego celu – wstawiania cacheowanego szablonu do szablonu który nie jest cacheowany. Aby lepiej to zrozumieć proszę przyjrzeć się poniższym kawałkom kodu:

strona.tpl (nie jest cacheowana)

<HTML>
        <HEAD>
        </HEAD>
        <BODY>
                {$smarty.now|date_format}
                <HR>
                {insert file=’tematy_forum’}
        </BODY>
</HTML>

tematy_forum.tpl (ma być cacheowany)

<TABLE>
<TBODY>
{foreach from=$forum_latest_topics item=topic}
<TR>
        <TD>{$topic.title|truncate:30}</TD>
</TR>
{/foreach}
<TR><TD>{$smarty.now|date_format}</TD></TR>
</TBODY>
</TABLE>

I najważniejszy plik insert.tematy_forum.php który powinien zostać utworzony w katalogu funkcji Smarty’ego

Czytaj dalej tutaj (rozwija treść wpisu)
Czytaj dalej na blogu autora...

Autor wpisu: Athlan, dodany: 15.11.2008 11:57, tagi: internet, php

Rzadko bo rzadko, ale jestem zmuszony używać funkcji wordwrap(). Zawsze denerwowało mnie, że wordwrap łamie linię (ciągi znaków) a nie wyrazy (jak na to nazwa funkcji wskazuje). Czy aby na pewno nazwa funkcji jest trafna?

Zwraca łańcuch str zawinięty w kolumny o odpowiedniej ilości znaków określonej przez opcjonalny parametr szerokość . Linia jest łamana przy użyciu (opcjonalnego)parametru break .

Niestety, potrzebowałem załamać wyrazy, a nie linie. Z pomocą przychodzi preg_replace(). Założenie jest takie, że wyrazem jest każdy ciąg znaków nie zawierający spacji. Łamiemy nawet ciągi liczb oraz znaków specjalnych, niekoniecznie zawierających litery:

function _wordwrap($sString, $iLimit = 32)
{
  return preg_replace('/([^ ]{’ . (int)$iLimit . ‘})/’, ‘$1 ‘, $sString);
}

Paradoksy w nazewnictwie się zdarzają.

Autor wpisu: Diabl0, dodany: 13.11.2008 13:06, tagi: zend_framework

Przy pracy nad serwisami o dużym obciążeniu bardzo ważną sprawą jest optymalizacja i keszowanie danych. Zend_Cache na szczęście wybitnie nam w tym pomaga, a od wersji 1.7 dodatkowo oferuje kilka ciekawych nowości.

Zacznijmy od Zend_Cache_Backend_TwoLevels - dzięki niemu w prosty sposób można połączyć zalety szybkich mechanizmów cache bazujących na pamięci (Memcache, APC), keszów stałych (pliki, baza danych), oraz pozwala na używanie wygodnego i potężnego mechanizmu tagowania w połączeniu backendami pamięciowymi które natywnie nie mają wsparcia dla tagów.

Poniżej znajduje się przykładowy plik INI konfigurujący TwoLevels dla mechanizmów File i Memcache:

[global]
;; -------------------- CACHE (two level) ------------------
cache.frontend = Core
cache.backend = Two Levels
cache.frontendOptions.automatic_serialization = true

cache.backendOptions.stats_update_factor = 10
cache.backendOptions.auto_refresh_fast_cache = true

cache.backendOptions.slow_backend = File
cache.backendOptions.slow_backend_options.cache_dir = ../temp
cache.backendOptions.slow_backend_custom_naming = false
cache.backendOptions.slow_backend_autoload = false

cache.backendOptions.fast_backend = Memcached
cache.backendOptions.fast_backend_options.servers.1.host = 10.0.0.1
cache.backendOptions.fast_backend_options.servers.1.port = 11211
cache.backendOptions.fast_backend_options.servers.1.persistent = true
cache.backendOptions.fast_backend_options.servers.2.host = 10.0.0.2
cache.backendOptions.fast_backend_options.servers.2.port = 11211
cache.backendOptions.fast_backend_options.servers.2.persistent = true
cache.backendOptions.fast_backend_custom_naming = false
cache.backendOptions.fast_backend_autoload = false

Przy okazji widać też sposób konfiguracji wielu serwerów Memcache, z czyn niektórzy mają problemy.

I w PHP:

$config = new Zend_Config_Ini ( '../configs/global.ini', 'global' );
Zend_Registry::set ( 'config', $config );

$cache = Zend_Cache::factory (
    $config->cache->frontend,
    $config->cache->backend,
    $config->cache->frontendOptions->toArray(),
    $config->cache->backendOptions->toArray()
);

Zend_Registry::set ( 'cache', $cache );

A teraz sposób użycia w przykładowym modelu, przy okazji z użyciem innej nowości 1.7 (notabene, mojej propozycji ;)):

/**
 * Rekord tabeli maternity_models_Maternity
 *
 * @category   App
 * @package    App_Maternity
 * @subpackage Models
 */

class maternity_models_Maternity_Row extends Mao_Db_Table_Row
{

    /**
     * Przechowuje koszt odbytych wizyt
     *
     * @var integer
     */
    private $_visitsCost = false;

    /**
     * Zwraca koszt odbytych wizyt
     *
     * @return integer
     */
    public function getVisitsCost()
    {

        if ( $this->_visitCost != false ) {
            return $this->_visitCost;
        }      

        /* @var $cache Zend_Cache_Core */
        $cache = Zend_Registry::get( 'cache' );
        $cache_id = 'maternity_models_Maternity_Row_visitsCost_' . $this->_data['maternity_id'];

        if(  ! $this->_visitsCost = $cache->load( $cache_id ) )
        {
            $visits = $this->getVisits();
            $ret = 0;

            foreach( $visits as $row )
            {
                $ret = $ret + $row->visit_cost;
            }
            $this->_visitsCost = $ret;

            $cache->save( $this->_visitsCost, $cache_id,
                array(
                    'maternity',
                    'maternity_models_Maternity' ,
                    'maternity_models_Maternity_Row_' . $this->_data['maternity_id']
                ),
                604800
            );
        }
        return $this->_visitsCost;
    }

    /**
     * Allows post-update logic to be applied to row.
     * Subclasses may override this method.
     *
     * @return void
     */
    protected function _postUpdate()
    {
        // Czyścimy cache updatowanego rekordu
        /* @var $cache Zend_Cache_Core */
        $cache = Zend_Registry::get( 'cache' );

        if ( isset( $this->_data['maternity_id'] ) ) {
            $cache->clean(
                Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG,
                'maternity_models_Maternity_Row_' . $this->_data['maternity_id']
            );
        }
        return parent::_postUpdate();
    }

}

W powyższym przykładzie tryb Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG usuwa z cache wszystkie rekordy zawierające podany tag.

Przy okazji wpadł mi jeszcze jeden pomysł na kolejny “tryb” czyszczenia - czyszczący rekordy zawierające wszystkie podane tagi, ale nad tym pomyślę już później.

Related posts

Autor wpisu: widmogrod, dodany: 12.11.2008 22:09, tagi: zend_framework, php

Zend Framework umożliwia specjalizację finalnego widoku(layoutu) dla każdej z akcji poprzez tzw. helpery("pomocniki") widoku. Jak to się dzieje odsyłam do podręcznika.Wszystko działało idealnie do dnia gdy nadeszła chwila integracji stronki z Google Maps API.W widoku akcji korzystam z wymiotnego w temacie pomocnika:$this->headScript()->appendFile('http://maps.google.com/maps?file=api&v=2&key='.$this->apiKey);nagłówek strony produkuje mi coś takiego:<script src="http://maps.google.com/maps?file=api&amp;amp;v=2&amp;amp;key=ABQIAAAAnCqO9l1WMOgTCJlg9kVlMRREqxHjot-MVdGv4W7rNtdAWxNh4hS2-gRBnLrWFzhAC8SpBzYmGVYZgA" type="text/javascript"/>a powinien<script src="http://maps.google.com/maps?file=api&amp;v=2&amp;key=ABQIAAAAnCqO9l1WMOgTCJlg9kVlMRREqxHjot-MVdGv4W7rNtdAWxNh4hS2-gRBnLrWFzhAC8SpBzYmGVYZgA" type="text/javascript"></script>jak łatwo zauważyć problem leży w &amp; -> &.Inicjowanie map googla tej zmiany nie lubi. Problem tkwi w funkcji htmlspecialchars, użytej w Zend_View_Helper_HeadScript::append() itd.Żeby nie ingerować w kod w/w klasy można zastosować funkcję htmlspecialchars_decode w szablonie w następujący sposób...// &amp; -> & becouse gmaps crash!print htmlspecialchars_decode($this->headScript());.. i wszystko działa, tak jak powinno od samego początku.

Czytaj dalej tutaj (rozwija treść wpisu)
Czytaj dalej na blogu autora...

Autor wpisu: Zyx, dodany: 06.11.2008 19:17, tagi: php

Zabrałem się dzisiaj za rozeznanie, jak tu spakować biblioteki OPL do postaci archiwów PHAR tak, by wciąż zachować ich własności. Z tego powodu musiałem napisać m.in. skrypt pakujący, a z niego zrodził się pomysł skrobnięcia paru słów na blogu pokazujących, jak to się robi. Tak więc do dzieła!

Autor wpisu: stormfly, dodany: 25.10.2008 11:29, tagi: sql

Czas płynie nieubłaganie, a jakiekolwiek nadzieje, że będzie trochę więcej czasu przy skończeniu kolejnego projektu rozwijają się bardzo szybko. Obiecałem sobie jednak, że postaram się umieszczać przynajmniej jeden wpis na miesiąc, aby mój blog nie podzielił losu innych...

Autor wpisu: widmogrod, dodany: 17.10.2008 01:14, tagi: php, eclipse

PHP Development Tools w wersji 2.0 ujrzy światło dzienne 29 grudnia 2008 roku, ale to mi nie przeszkadza by pracować na wersji rozwojowej (:.Poniżej przedstawiam przetłumaczony poradnik instalacji PDT2.0 na najnowszej wersji mojego ulubionego IDE Eclipse.Pobieramy i rozpakowujemy Eclipse IDE for Java EE Developers (162 MB) do wybranego katalogu.Do pełni szczęścia brakuje nam jeszcze trzech paczek.
  1. DLTK (Integration) link bezpośredni do paczki
  2. PDT2.0 (2.0.0 Integration Builds) link bezpośredni
  3. WTP (tą paczkę sugeruję pominąć, bo w Java EE już jest!)
Powyższe paczki rozpakowujemy i dodajemy do managera pakietów
  1. Help > Software Updates... > Available Software
  2. Add Site... > Local > dodajemy katalog rozpakowanego pakietu DLTK
  3. Add Site... > Local > dodajemy katalog rozpakowanego pakietu PDT2.0
  4. Add Site... > Local > dodajemy katalog rozpakowanego pakietu WTP
  5. Gdy odświeżenie listy pakietów nie wykona się automatycznie - wciskamy "Refresh"
  6. Zaznaczamy pakiet the "Dynamic Languages Toolkit - Core Frameworks .."
  7. Zaznaczamy pakiet "PDT Feature"
  8. Zaznaczamy "WTP Feature" (jeżeli jeszcze nie jest jest zainstalowany)
  9. Klikamy "Install"
Gdy wszystko przebiegnie pomyślnie uruchamiamy ponownie Eclipse i mamy PHP Development Tools w wersji 2.0 :). Teraz tylko New PHP Project i zabawa się zaczyna (:.Uwaga na koniec, w Eclipse Ganymede w okienku "Software Updates" jest możliwość wpisywania nazw pakietów należy w tym polu wpisywać pełne nazwy pakietów do instalacji a nie ich akronimy tj. zamiast DLTK należy wpisać Dynamic Languages Toolkit
Wszystkie wpisy należą do ich twórców. PHP.pl nie ponosi odpowiedzialności za treść wpisów.