Autor wpisu: Diabl0, dodany: 26.11.2008 12:15, tagi: zend_framework, php
Dzisiaj ponad 2 godziny spędziłem na dochodzeniu dlaczego Zend_Auth zapisuje do sesji “okrojony” rekord usera (bazujący na Zend_Db_Table_Row).
Rekord ten zawierał garść dodatkowych informacji (jak na przykład lista grup do których należy user) wypełnianych przed przekazaniem go do Zend_Auth_Storage, które jednak nie zapisywały się tam. Dopiero dokładna analiza wykazała że winowajcą była metoda __sleep() w Zend_Db_Table_Row_Abstract
Lekarstwem okazało się nadpisanie w modelu usera ( Main_Model_Users_Row ) tej metody, i tak teraz mój model wygląda mniej więcej tak:
class Main_Models_Users_Row extends Mao_Db_Table_Row { /** * tablica z row'ami grup usera * * @var array */ private $_groups; /** * trzeba przeciazyc sleepa i do tablicy $ret * dodac wszystkie pola, ktore maja byc zserializowane * * @return array */ public function __sleep() { $ret = parent::__sleep (); $ret [] = '_groups'; return $ret; } }
Related posts
Autor wpisu: Diabl0, dodany: 20.11.2008 09:46, tagi: php, zend_framework
Ostatnio po raz kolejny w czasie prac nad jednym z projektów stanąłem przed problemem generowania dokumentów do druku. I po raz kolejny automatycznie wybór padł na PDF. I znowu ( ) po raz kolejny stanąłem przed problemem jakiej klasy użyć aby ułatwić sobie zadanie. Tym razem mój wzrok padł na TCPDF i jak na razie spełnia on pokładane w nim nadzieje.
Poniżej natomiast znajdziecie mały wrapper aby korzystanie z TCPDF w Zend Framework było proste, wygodne i zgodne z standardami ZF.
Samą klasę znajdziecie na SF.net: http://sourceforge.net/project/showfiles.php?group_id=128076 - wystarczy ją pobrać i wrzucić do katalogu library/Mao/TCPDF/ (oczywiście Mao zamieniacie na własną nazwę).
Następnie wystarczy utworzyć prostą klasę Mao_TCPDF w library/Mao/TCPDF.php , a w niej:
<?php /** * This is a Zend Framework wrapper for TCPDF class for generating PDF documents without requiring external extensions. * * {@link http://www.tcpdf.org TCPDF project} has been originally derived in 2002 from the Public Domain {@link http://www.fpdf.org FPDF class} by Olivier Plathey, but now is almost entirely rewritten. * * * @category Mao * @package Mao_TCPDF * @author Krzysztof 'Diabl0' Szatanik * @copyright Copyright (c) 2008, MAO Group * @link http://www.tcpdf.org */ Zend_Loader::loadFile( 'tcpdf.php', '../library/Mao/TCPDF/', true ); /** * This is a Zend Framework wrapper for TCPDF class for generating PDF documents without requiring external extensions. * * {@link http://www.tcpdf.org TCPDF project} has been originally derived in 2002 from the Public Domain {@link http://www.fpdf.org FPDF class} by Olivier Plathey, but now is almost entirely rewritten. * * @category Mao * @package Mao_TCPDF * @author Krzysztof 'Diabl0' Szatanik * @copyright Copyright (c) 2008, MAO Group * @link http://www.tcpdf.org */ class Mao_TCPDF extends TCPDF { /** * @var alias for total number of pages */ protected $AliasNbPages = '{nb}'; /** * This is the class constructor. * * It allows to set up the page format, the orientation and the measure unit used in all the methods (except for the font sizes). * * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or Portrait (default)</li><li>L or Landscape</li></ul> * @param string $unit User measure unit. Possible values are:<ul><li>pt: point</li><li>mm: millimeter (default)</li><li>cm: centimeter</li><li>in: inch</li></ul><br />A point equals 1/72 of inch, that is to say about 0.35 mm (an inch being 2.54 cm). This is a very common unit in typography; font sizes are expressed in that unit. * @param mixed $format The format used for pages. It can be either one of the following values (case insensitive) or a custom format in the form of a two-element array containing the width and the height (expressed in the unit given by unit).<ul><li>4A0</li><li>2A0</li><li>A0</li><li>A1</li><li>A2</li><li>A3</li><li>A4 (default)</li><li>A5</li><li>A6</li><li>A7</li><li>A8</li><li>A9</li><li>A10</li><li>B0</li><li>B1</li><li>B2</li><li>B3</li><li>B4</li><li>B5</li><li>B6</li><li>B7</li><li>B8</li><li>B9</li><li>B10</li><li>C0</li><li>C1</li><li>C2</li><li>C3</li><li>C4</li><li>C5</li><li>C6</li><li>C7</li><li>C8</li><li>C9</li><li>C10</li><li>RA0</li><li>RA1</li><li>RA2</li><li>RA3</li><li>RA4</li><li>SRA0</li><li>SRA1</li><li>SRA2</li><li>SRA3</li><li>SRA4</li><li>LETTER</li><li>LEGAL</li><li>EXECUTIVE</li><li>FOLIO</li></ul> * @param boolean $unicode TRUE means that the input text is unicode (default = true) * @param String $encoding charset encoding; default is UTF-8 */ public function __construct( $orientation = 'P' , $unit = 'mm' , $format = 'A4' , $unicode = true , $encoding = "UTF-8" ) { parent::__construct( $orientation, $unit, $format, $unicode, $encoding ); // Zawsze o tym zapominam, więc poleciało do konstruktora $this->AliasNbPages(); } /** * Zwraca szerokość strony * * @return float */ public function getPageWidth() { return $this->w - $this->rMargin - $this->x; } /** * Prints a cell (rectangular area) with optional borders, background color and character string. The upper-left corner of the cell corresponds to the current position. The text can be aligned or centered. After the call, the current position moves to the right or to the next line. It is possible to put a link on the text.<br /> * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting. * @param float $w Cell width. If 0, the cell extends up to the right margin. * @param float $h Cell height. Default value: 0. * @param string $txt String to print. Default value: empty string. * @param mixed $border Indicates if borders must be drawn around the cell. The value can be either a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> * @param int $ln Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right (or left for RTL languages)</li><li>1: to the beginning of the next line</li><li>2: below</li></ul> Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0. * @param string $align Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align (default value)</li><li>C: center</li><li>R: right align</li><li>J: justify</li></ul> * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0. * @param mixed $link URL or identifier returned by AddLink(). * @param int $stretch stretch carachter mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if necessary</li><li>2 = forced horizontal scaling</li><li>3 = character spacing only if necessary</li><li>4 = forced character spacing</li></ul> * @since 1.0 * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), AddLink(), Ln(), MultiCell(), Write(), SetAutoPageBreak() */ public function Cell( $w , $h = 0 , $txt = '' , $border = 0 , $ln = 0 , $align = '' , $fill = 0 , $link = '' , $stretch = 0 ) { //html_entity_decode("&amp;euro;", ENT_COMPAT, "UTF-8"); if( $txt != '' ) { $txt = str_replace( '&amp;euro;', html_entity_decode( "&amp;euro;", ENT_COMPAT, "UTF-8" ), $txt ); } parent::Cell( $w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch ); } } ?>
W zasadzie wszystko powinno być jasne, ale na koniec jeszcze 2 uwagi.
1 - Własnego konstruktora używam z 2 powodów - AliasNbPages o którym zawsze zapominam w samym kodzie, i ładowanie własnego konfiga z Zend_Config (ten fragment dla czytelności pominąłem na powyższym listingu).
2 - metoda Cell - została lekko nadpisana z powodu często używanego przeze mnie znaku €. Po prostu wygodnie jest mi w kodzie używać znaku entity & euro ;.
Related posts
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
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
$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;v=2&amp;key=ABQIAAAAnCqO9l1WMOgTCJlg9kVlMRREqxHjot-MVdGv4W7rNtdAWxNh4hS2-gRBnLrWFzhAC8SpBzYmGVYZgA" type="text/javascript"/>
a powinien<script src="http://maps.google.com/maps?file=api&v=2&key=ABQIAAAAnCqO9l1WMOgTCJlg9kVlMRREqxHjot-MVdGv4W7rNtdAWxNh4hS2-gRBnLrWFzhAC8SpBzYmGVYZgA" type="text/javascript"></script>
jak łatwo zauważyć problem leży w & -> &.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...// & -> & becouse gmaps crash!print htmlspecialchars_decode($this->headScript());
.. i wszystko działa, tak jak powinno od samego początku.