วันพุธที่ 29 ธันวาคม พ.ศ. 2553

jquery selector

tag_name เช่น a หมายถึง <a></a> ทั้งหมดใน html document

id_name เช่น #link1 ตรงกับ <tag id="link1"></tag>

class_name เช่น .class_name ตรงกับ <tag class="class_name"></tag>

[attribute_name] ตรงกับ tag ใดๆ ที่มี attribute "attribute_name" ตัวอย่างเช่น [name] คือทุกแทกที่มี attribute href เช่น <a href="test"></a>

[attribute_name="test"] ตรงกับ tag ใดๆ ที่มี attribute "attribute_name" มีค่าเท่ากับ test ตัวอย่างเช่น [href] คือทุกแทกที่มี attribute href="test" เช่น <a href="test"></a>

[attribute_name^="test"] ตรงกับ tag ใดๆ ที่มี attribute "attribute_name" มีค่าเริ่มต้นเท่ากับ test ตัวอย่างเช่น [href] คือทุกแทกที่มี attribute href เริ่มต้นด้วยคำว่า test เช่น <a href="test_12345"></a>

parent descendant ตัวอย่างเช่น #x1 a
คือ tag a ทั้งหมดที่อยู่ใน tag ที่มี id="x1" เช่น <div id="x1"><b>test</b><a>ss</a><a>ss2</a><span><a>ss3</a></span></div> จะหมายถึง <a>ss</a><a>ss2</a> และ <a>ss3</a>

parent > children ตัวอย่างเช่น #x1 > a
คือ tag a ทั้งหมดที่อยู่ใน tag ที่มี id="x1" เช่น <div id="x1"><b>test</b><a>ss</a><a>ss2</a><span><a>ss3</a></span></div> จะหมายถึง <a>ss</a><a>ss2</a>

เราสามารถรวมเอาหลายๆ selector มารวมกันเพื่อ tag ที่เฉพาะเจาะจงมากขึ้นเช่น
.example[href^="http://"][target="_blank"] span
เป็นการเลือกทุก tag span ทั้งหมด
ที่อยู่ภายใน tag ซึ่งมี class เท่ากับ example และ tag นั้นยังมีมี attribute href เริ่มต้นด้วย http:// และ attribute target เท่ากับ blank


ไว้จะมาเขียนเพิ่มอีกครับ ..

jquery selector

วันเสาร์ที่ 25 ธันวาคม พ.ศ. 2553

jquery enlarge image by num



<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=tis-620" />
<title>enlarge image by num</title>
<style type="text/css">
* {margin:0;padding:0}
</style>
<script type="text/javascript" src="http://code.jquery.com/jquery-latest.js"></script>
<script>
//<![CDATA[
$(function(){
$('.imgx').hover(function(){
var w = 200;
var h = 220;
var d = 600;//duration
var imgx = $(this);
$('.imgy').remove();
var imgy = $('<img class="imgy" src="'+$(this).attr('src')+'"/>').appendTo('body');
imgy.css({
position: 'absolute',
left: imgx.offset().left,
top: imgx.offset().top,
width: imgx.width(),
height: imgx.height()
}).mouseout(function(){
$('.imgy').remove();
}).click(function(){
$('.imgy').remove();
});
imgy.animate({
left: imgx.offset().left - (w/2),
top: imgx.offset().top - (h/2),
width: w+'px',
height: h+'px'
},d);
},function(){});
});
//]]>
</script>
</head>

<body>

<div style="width:500px;height:350px;padding:100px;border:solid green 1px;">
<?php foreach(range(1,117) as $i):?>
<img src="http://www.google.com/friendconnect/scs/images/NoPictureDark32.jpg" class="imgx" />
<?php endforeach;?>
</div>

</body>
</html>

วันพฤหัสบดีที่ 23 ธันวาคม พ.ศ. 2553

php start pyramid


<div style="font-family:fixedsys">
<?php for($max=3;$max<=33;$max+=10) for($i=1;$i<=$max; $i+=2) echo str_repeat('&nbsp;', ceil($max/2) - ceil($i/2)), str_repeat('*',$i), '<br','>'; ?>
</div>

<div align="center">
<?php for($max=3;$max<=33;$max+=10) for($i=1;$i<=$max; $i+=2) echo str_repeat('*',$i), '<br','>'; ?>
</div>

วันพุธที่ 22 ธันวาคม พ.ศ. 2553

update multiple record


<?php

if ($_SERVER['REQUEST_METHOD'] == 'POST'){
echo 'doraemon,nobita,pokemon,id<br','>';
foreach((array)$_POST['data'] as $pk=>$d){
echo $d['doraemon'],',',$d['nobita'],',',$d['pokemon'],',',$pk,'<br','>';
//เอาข้อมูลนี้ไปสร้างคำสั่ง update ...
}
die('save');
}

?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-874" />
<title>update multiple record</title>
</head>

<body>

<form id="form1" name="form1" method="post" action="">
<?php foreach(range(1,2) as $i):?>
<input type="text" name="data[<?php echo $i;?>][doraemon]" />
<input type="text" name="data[<?php echo $i;?>][nobita]" />
<input type="text" name="data[<?php echo $i;?>][pokemon]" />
<?php echo '<br','>'; endforeach;?>
<input type="submit" name="button" id="button" value="Submit" />
</form>

</body>
</html>

วันอาทิตย์ที่ 19 ธันวาคม พ.ศ. 2553

radio button validation


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-874" />

<title>radio check demo</title>

<script type="text/javascript">
function test(f){
var t1;for(var i=0;i<f.q1_1.length;i++) if (f.q1_1[i].checked == true) t1 = true;
if (!t1) {alert('radio group 1 empty.'); f.q1_1[0].focus();return false;}

var t2;for(var i=0;i<f.q1_2.length;i++) if (f.q1_2[i].checked == true) t2 = true;
if (!t2) {alert('radio group 2 empty.'); f.q1_2[0].focus();return false;}

return true;
}
</script>
</head>


<body>

<form method="post" action="" onsubmit="return test(this);" name="f1" id="f1">
1.1 ???
<input name="q1_1" type="radio" id="rdo5" value="5" >
<input name="q1_1" type="radio" id="rdo4" value="4">
<input name="q1_1" type="radio" id="rdo3" value="3">
<input name="q1_1" type="radio"id="rdo2" value="2">
<input name="q1_1" type="radio" id="rdo1" value="1">
1.2 ???
<input type="radio" name="q1_2" id="rdo5_2" value="5">
<input type="radio" name="q1_2" id="rdo4_2" value="4">
<input type="radio" name="q1_2" id="rdo3_2" value="3">
<input type="radio" name="q1_2" id="rdo2_2" value="2">
<input type="radio" name="q1_2" id="rdo1_2" value="1">

<input type="submit" />

</form>

</body>
</html>


เช็คหลายๆ radiobox

<?php $radio_size = 3;?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-874" />

<title>radio check demo</title>

<script type="text/javascript">

var radio_size = <?php echo $radio_size;?>;
function radio_checked(rdo){
var t1=false;for(var i=0;i<rdo.length;i++) if (rdo[i].checked == true) t1 = true;
return t1;
}

function test(f){
for(var i=1;i<=radio_size;i++)
if (!radio_checked(f['q'+i])){
alert('radio group '+i+' empty.');
f['q'+i][0].focus();
return false;
}
return true;
}
</script>
</head>


<body>

<form method="post" action="" onsubmit="return test(this);" name="f1" id="f1">

<?php foreach(range(1,$radio_size) as $i):?>
<hr /><?php echo $i;?> ???
<input name="q<?php echo $i;?>" type="radio" value="5" >
<input name="q<?php echo $i;?>" type="radio" value="4">
<input name="q<?php echo $i;?>" type="radio" value="3">
<input name="q<?php echo $i;?>" type="radio" value="2">
<input name="q<?php echo $i;?>" type="radio" value="1">
<?php endforeach;?>

<input type="submit" />

</form>

</body>
</html>

layout-views or master-contents page

master-page.php

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-874" />
<link type="text/css" rel="stylesheet" href="style.css"/>
<title>{$title}</title>
{$head}
</head>

<body>
<h1>Welcome to my site</h1>
<table width="800" border="2"><tr><td bgcolor="#FFCC00">
{$content1}
</td><td bgcolor="#FFFFCC">
{$content2}
</td></tr>
</table>
<hr/>
easy master page by num |
<a href="normal-page.php">page1</a> |
<a href="normal-page2.php">page2</a>
</body>
</html>


normal-page.php

<?php require 'util.php'; $MasterPage = 'master-page.php';?>
<?php if (0):?><meta http-equiv="Content-Type" content="text/html; charset=windows-874" />
<link type="text/css" rel="stylesheet" href="style.css"/><?php endif;?>

<?php sb('title'); echo 'test-title'; eb();?>

<?php sb('head');?>
<script type="text/javascript">
window.onload = function(){
document.getElementById('test').innerHTML = 'javascript text';
}
</script>
<?php eb();?>

<?php sb('content1');?>
<b>hello world</b>
<div id="test"></div>
<?php eb();?>

<?php sb('content2');?>
lksldfjksjdklfjk lsjdlkfj s dfjsldkjfks jdklfjkljsdl fklsjdklfjlksd
<?php eb();?>


<?php render();?>


normal-page2.php

<?php require 'util.php'; $MasterPage = 'master-page.php';?>
<?php if (0):?><meta http-equiv="Content-Type" content="text/html; charset=windows-874" />
<link type="text/css" rel="stylesheet" href="style.css"/><?php endif;?>

<?php sb('title'); echo 'สวัสดีครับ'; eb();?>

<?php sb('head');?>
<?php eb();?>


<?php sb('content1');?>
page2
<?php eb();?>

<?php sb('content2');?>
this is page 2.
<?php eb();?>



<?php render();?>


util.php

<?php

$_vars = array();
function sb($name){
static $sName;
if (empty($name)) return $sName;
$sName = $name;
ob_start();
}
function eb(){
global $_vars;
$name = sb(0);
$_vars[$name] = ob_get_clean();
}
function render(){
global $_vars;
ob_start();
require ($GLOBALS['MasterPage']);
$page = ob_get_clean();
foreach($_vars as $name=>$var)
$page = str_replace('{$'.$name.'}',$var,$page);
echo $page;
}

?>



/* CSS Document */

*{margin:0;padding:0;}
body {background-color:#ccc;}

วันศุกร์ที่ 17 ธันวาคม พ.ศ. 2553

popup windows close and refresh opener window

test.php

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

<title>test</title>
</head>

<body>

<div><?php echo date('H:i:s');?></div>

<button onclick="var w=window.open('popup.php','popup_name','width=850,height=300');w.focus();">Open Popup</button>

</body>
</html>


popup.php

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

<title>pop up</title>
</head>

<body>

<?php if (!empty($_POST['test'])) echo "name={$_POST['test']}";?>

<form method="post">

<input type="text" name="test" />
<input type="submit"/>
</form>

<button onclick="opener.location=opener.location.toString();self.close();">Close</button>

</body>
</html>

วันพฤหัสบดีที่ 16 ธันวาคม พ.ศ. 2553

using php array


<?php
function block_split($s){
$pos = strpos($s,'[');
$has_b = is_int($pos);
if ($has_b){
$a = array();
$test = preg_match_all('/(\[[^]]*\])([^[]*)/s',$s,$ms,PREG_SET_ORDER);
foreach($ms as $m){
$a[] = $m[1];
$a[] = $m[2];
}
array_unshift($a, substr($s,0,$pos));
} else {
$a = array($s);
}
$a = array_map('trim',$a);
$a = array_diff($a,array(''));
$a = array_values($a);
return $a;
}

$s = 'xxx [metal] [plating] pine apple [test] test';
$a = block_split($s);
echo '<pre>',var_export($a,true),'</pre>';


?>

วันพุธที่ 15 ธันวาคม พ.ศ. 2553

ส่งเมล์ให้เป็นภาษาไทยโดยใช้ php send mail UTF-8

โปรแกรมนี้เป็นโปรแกรมที่ส่ง mail แบบ multipart โดยส่งทั้ง format text/plain และ text/html ไปพร้อมๆ กันครับ จึงไม่ต้องกังวลว่าผู้รับเมล์จะอ่าน html หรือ text อย่างใดอย่างหนึ่งไม่ได้
ผ่านการทดสอบจาก webisite email ดังต่อไปนี้ hotmail.com, yahoo.com, gmail.com และ fastmail.fm

( ต้องบันทึกโค้ดนี้เป็น utf8 ครับถึงจะได้ค่าภาษาไทยที่ถูกต้อง )

<?php

//define('DEBUG',true);

function ct($ct,$a){
$s = "Content-Type: $ct";
foreach($a as $k=>$v){
$s .= ";\r\n\t$k=\"$v\"";
}
return "$s\r\n";
}
function cte($cte){
return "Content-Transfer-Encoding: $cte\r\n";
}
function enc($s){
return '=?UTF-8?B?'.base64_encode($s).'?=';
}

function mail_utf8($to,$from,$subject,$text,$html)
{
$hash = md5(date('r', time()));
$b1 = 'b1_'.$hash; $bb1 = "\r\n--$b1\r\n"; $bb1e = "\r\n--$b1--\r\n";
$b2 = 'b2_'.$hash; $bb2 = "\r\n--$b2\r\n"; $bb2e = "\r\n--$b2--\r\n";
if (strpos($from,'<')){
list($name,$email)=explode('<',$from);
$from = enc($name).' <'.$email;
}
if (strpos($to,'<')){
list($name,$email)=explode('<',$to);
$to = enc($name).' <'.$email;
}
$h = "From: $from\r\n";
$h .= "Reply-To: $from\r\n";
$h .= "MIME-Version: 1.0\r\n";
$h .= ct('multipart/related',array(
'type'=>'text/html',
'boundary'=>$b1
));
$m = $bb1;
$m .= ct('multipart/alternative',array('boundary'=>$b2));
$m .= $bb2;
$m .= ct('text/plain',array('charset'=>'UTF-8'));
$m .= cte('8bit');
$m .= "\r\n$text\r\n";
$m .= $bb2;
$m .= ct('text/html',array('charset'=>'UTF-8'));
$m .= cte('8bit');
$m .= "\r\n$html\r\n";
$m .= $bb2e;
$m .= $bb1e;
$subject = enc($subject);

if (defined('DEBUG')){
echo "<pre>\r\n";
echo "$to\r\n$subject\r\n$h\r\n$m";
echo '</pre>';
} else {
return @mail( $to, $subject, $m, $h );
}
}

mail_utf8
(
'คุณสวัสดีครับ <sendto@sendtodomainname.com>',
'คุณสวัสดีค่ะ <myemail@mydomainname.com>',
'หัวข้อ',
'text ครับ',
'<body><b>html</b>ครับ</body>'
);

?>

วันจันทร์ที่ 13 ธันวาคม พ.ศ. 2553

sourcecode from book

ซอร์สโค้ดแจกฟรี ที่ไหนจะดีเท่ากับจากหนังสือ อิๆ

http://www.witty.net/download.html
http://www.ktpbook.com/Download/search_download.asp
http://www.successmedia.com/product.php?ke...sub&key2=13
http://downloads.se-ed.com/
http://www.soft-express.co.th/index.htm
http://www.provision.co.th/download/index.html
http://www.infopress.co.th/download.php

ภาค 2 หนังสือต่างประเทศ
http://www.apress.com/book/sourcecode
เลือกดาวน์โหลดเฉพาะที่สนใจนะครับ เพราะเค้านับจำนวน ip ที่ connect ถ้าดาวน์โหลดเยอะๆ จะโหลดไม่ได้

ฟรี ebook PHP4 478 หน้า
http://www.apress.com/book/downloadfile/11

http://www.packtpub.com/free
ของฟรีเพียบ แต่ต้องป้อน email

เยอะจัด
http://www.wrox.com/WileyCDA/Section/Brows....id-105127.html

บางอันก็ฟรีบางอันก็ไม่ฟรีครับแต่คุณภาพจริงๆ
http://www.manning.com/

=========================================
รวม ebook ฟรีไม่ละเมิดลิขสิทธิ์ครับ

http://www.freetechbooks.com/
This site lists free online computer science, engineering and programming books, textbooks and lecture notes, all of which are legally and freely available over the Internet.
Throughout this site, other terms are used to refer to a book, such as ebook, text, document, monogram or notes.
All the books listed in this site are freely available, as they are hosted on websites that belong to the authors or the publishers.
Please note that
( a ) we do not host pirated books and
( b ) we do not link to sites that host pirated books and © we do not even link to sites that link to sites that host pirated books.

http://freecomputerbooks.com/about_books.html
FreeComputerBooks.com consists of a huge collection of free online Computer, Programming, Mathematics, Technical Books, Lecture Notes and Tutorials. It is very well categorized by topics, with 12 top level categories, and over 150 sub-categories. It has both a pattern search engine and a keywords search engine, the later is powered Google.
All the books listed in this web site are only links to other web sites all over the world, except few which we got special permission from authors or publishers. No copyright violation is within this site. In fact, over 95% of the books listed in this site are the search results by Google, Yahoo, Live.com, Ask.com, and other search engines. The rest of links were recommanded by readers like you.


http://www.intelligentedu.com/blogs/post/F...ogramming-Books
http://oreilly.com/openbook/
http://www.techbooksforfree.com/
http://selfmadescholar.com/b/2008/01/16/10...-science-books/
http://www.selfmadescholar.com/classes.php?tag=computers
http://portal.acm.org/toc.cfm?id=SERIES114...=ACM&dl=ACM
http://www.freebookcentre.net
http://en.wikibooks.org/wiki/Wikibooks:Computing_department

หนังสือวิชาการ -__-'
http://www.cra.org/Activities/craw/booklist/index.php
http://standish.stanford.edu/bin/page?forward=home

the change calcuation


<?php
function return_money($m){
$debug = 1;
$m *= 100;
$x100 = array(
'50000'=>'แบ้งค์500',
'10000'=>'แบ้งค์100',
'5000'=>'แบ้งค์50',
'2000'=>'แบ้งค์20',
'1000'=>'เหรียญ10',
'500'=>'เหรียญ5',
'200'=>'เหรียญ2',
'100'=>'เหรียญบาท',
'50'=>'เหรียญ50สตางค์',
'25'=>'เหรียญ25สตางค์',
);
$n = array();
if ($debug) echo 'จำนวนเงิน '. ($m/100) . ' จะต้องทอน <br'.'>';
foreach($x100 as $x=>$name){
$r = $m % $x;
$idx = (string)($x/100);
$n[$idx] = ($m - $r)/$x;
$m = $r;
if($debug) if (!empty($n[$idx])) echo $name . ' จำนวน ' .$n[$idx]. '<br'.'>';
}
return $n;
}

$exchange = '451.75';
$moneys = return_money($exchange);
print_r($moneys);

?>

วันพฤหัสบดีที่ 9 ธันวาคม พ.ศ. 2553

remove some value from array


<?php
$a = array(11,5,0,2,0,1);
$a = array_diff($a,array(0)); //ลบสมาชิกที่มีค่า 0 ออก .... ใช้คำสั่งอันนี้ หรือ
//$a = array_filter($a); //ลบสมาชิกที่มีค่า 0 ออก .... ใช้คำสั่งอันนี้ ก็ได้
sort($a,SORT_NUMERIC); //เรียงลำดับ
echo '<pre>';print_r($a);echo '</pre>';
?>

วันพุธที่ 8 ธันวาคม พ.ศ. 2553

replace bbcode to syntaxhighlighter


<?php
function view($t){
$br = '<!--x-->';
$t = str_replace(array("\r\n","\r","\n"),$br,htmlspecialchars($t));
$sr = array(
'/\[php\](.*?)\[\/php]/is' => '<pre class="brush: php;">%s</pre>',
'/\[js\](.*?)\[\/js]/is' => '<pre class="brush: js;" >%s</pre>',
'/\[html\](.*?)\[\/html]/is' => '<pre class="brush: html;" >%s</pre>',
);
foreach($sr as $s => $r){
preg_match_all($s, $t, $ms, PREG_SET_ORDER);
foreach($ms as $m) $t = str_replace($m[0], sprintf($r,str_replace($br,"\r\n",$m[1])), $t);
}
$t = str_replace($br,'<br'.'>',$t);
return $t;
}

echo view(
'[php]
<?php echo "test";?>
[/php]
ทดสอบ

ว่าทำงานได้หรือยัง

test
[php] xxx[/php]
[js]var j=1;[/js]
[html]<p>test</p>[/html]'
);

?>

วันอังคารที่ 7 ธันวาคม พ.ศ. 2553

การใช้ preg_replace_callback

สำหรับโค้ดตัวนี้สำหรับแปลงข้อมูล bbcode จาก [code][/code] เป็นโค้ดสำหรับแสดงใน
syntax highlighter ครับ

function _hilighter($m){
return '<pre name="code" class="php">'.htmlspecialchars($m[1]).'</pre>';
}
$s=<<<html
[code]
<?php
echo 'hello php.';
?>
[/code]
html;
$s = preg_replace_callback('/\[code\](.+?)\[\/code\]/s','_hilighter',$s);

window media player reference

http://www.w3schools.com/media/media_playerref.asp

function ภาษาไทย ที่จัดเก็บเป็น utf8 ใน php

สำหรับคนที่ใช้ภาษาไทย utf8 จะต้องเปลี่ยนจาก substr หรือ strpos ฯลฯ เป็น mb_??? ดังนี้ครับ


<?php
header('content-type:text/html;charset=utf-8');
mb_internal_encoding('UTF-8');
$s = 'ทดสอบ';
$d = 'ลองทดสอบ';
$pos = mb_strpos($d,$s);
echo mb_substr($d,0,$pos);
?>

CakePHP Coding Standard

Cake Developers จะใช้มาตรฐานการโค้ดดังต่อไปนี้

#การเพิ่มคุณสมบัติใหม่
ไม่ควรเพิ่มคุณสมบัติใหม่ โดยไม่ได้ทดสอบระบบซึ่งควรจะทำก่อนน้ำเข้าไปบันทึกยัง repository

#การจัดย่อหน้า
1 tab จะถูกใช้เพื่อจัดย่อหน้า

#โครงสร้างควบคุม
<?php
if ((expr_1) || (expr_2 )) {
    // action_1;
} elseif (!(expr_3) && (expr_4)) {
    // action_2;
} else {
    // default_action;
}
?>
- ในโครงสร้างควบคุมควรจะมี 1 ช่องว่างก่อนที่จะถึงวงเล็บเปิด และ 1 ช่องว่างระหว่างวงเล็บสุดท้ายกับ bracket {
- ใช้ brackets เสมอแม้ว่ามันไม่จำเป็น ซึ่งจะอ่านโค้ดได้ง่ายขึ้น
- การเปิด bracket ควรจะอยู่ในบรรทัดเดียวกับโครงสร้างควบคุม.
การปิด bracket ควรจะวางอยู่บนบรรทัดใหม่, และมันควรจะมีระดับย่อหน้าระดับเดียวกับ
โครงสร้างควบคุม. คำสั่งใน bracket ควรจะเริ่มในบรรทัดใหม่และโค้ดข้างในควรจะจัดย่อหน้าอยู่ในระดับสูงขึ้น
<?php
// wrong - no brackets, badly placed statement
if (expr) statement;

// wrong - no brackets
if (expr)
    statement;

// good
if (expr) {
    statement;
}
?>


# ternary operator
อย่าใช้ ternary operator (?). เราต้องการให้ cake code base อ่านได้ง่ายเท่าที่เป็นไปได้
และเราเชื่อว่าการใช้ full if-else จะทำให้มันดูดีกว่า, อ่านง่ายกว่าและสำคัญที่สุดคือ debug ได้ง่ายกว่า

# การเรียก Function
Function ควรจะถูกเรียกโดยไม่มีช่องว่างระหว่างชื่อ function และวงเล็บเปิด,
และมันควรจะมีช่องว่างระหว่างแต่ละ parameter ในการเรียกใช้ function

<?php
$var = foo($bar, $bar2, $bar3);
?>
อย่างที่คุณเห็นข้างบนควรจะมีช่องว่างหน้าและหลังเครื่องหมายเท่ากับ

#การประกาศ method
<?php
function someFunction($arg1, $arg2 = '') {
    if (expr) {
        statement;
    }
    return $var;
}
?>
parameter ที่มีค่า default, ควรจะวางอยู่ที่ตำแหน่งสุดท้ายใน parameter.
พยายามทำให้ function คืนค่าบางอย่าง อย่างน้อย true หรือ false - เพื่อสามารถระบุได้ว่า
function ทำงานสำเร็จหรือไม่

#การให้คำอธิบายโค้ด
comment ทั้งหมดควรจะเขียนโดยใช้ภาษาอังกฤษ, และวิธีที่จะอธิบายบล๊อกที่ถูกอธิบายได้ชัดเจน

comment สามารถมี phpDocumentor tags ดังต่อไปนี้
    * @access
    * @author
    * @copyright
    * @deprecated
    * @example
    * @ignore
    * @internal
    * @link
    * @see
    * @since
    * @tutorial
    * @version
    * inline {@internal}}
    * inline {@inheritdoc}}
    * inline {@link}}

PhpDoc tagas คล้ายกับ JavaDoc tags มาก. Tag จะถูกประมวลผลถ้ามันอยู่เป็นสิ่งแรกใน
บรรทัดของ DocBlock, ตัวอย่างเช่น:
<?php
 /**
 * Tag example.
 * @author this tag is parsed, but this @version is ignored
 * @version 1.0 this tag is also parsed
 */
?>

มีเพียงแค่ 3 inline tags ({@internal}, {@inheritdoc} และ {@link})
<?php
/**
 * Example of inline phpDoc tags.
 *
 * This function works hard with {@link foo()} to rule the world.
 */
function bar() {
}
 
/**
 * Foo function
 */
function foo() {
}
?>

# การ include file
เมื่อ include file class หรือ libraies, จะต้องใช้ require_once เท่านั้น

# PHP tags
ใช้ <?php ?> ไม่ใช้ <? ?>

# Functions
ตั้งชื่อทุกๆ function ในแบบ camelBack
<?php
function longFunctionName() {
}
?>

# Classes
ชื่อคลาสควรจะใช้ CamelCase, ตัวอย่างเช่น
<?php
class ExampleClass {
}
?>

# Variables
ชื่อตัวแปรควรจะอธิบายให้ชัดเจนเท่าที่เป็นไปได้, แต่ควรจะสั้นเท่าที่เป็นไปได้.
โดยปกติตัวแปรควรจะเริ่มต้นด้วย lowercase, และควรจะเขียนในแบบ camelBack
ในกรณีที่มีหลายคำ. สำหรับตัวแปรที่เป็น object ควรเริ่มต้นด้วยตัวอักษรตัวใหญ่
<?php
$user = 'John';
$users = array('John', 'Hans', 'Arne');

$Dispatcher = new Dispatcher();
?>

# Member visibility
เนื่องจากเราไม่สามารถใช้ PHP5 private และ protected keyword สำหรับ methods หรือ variables
เราเห็นด้วยกับการใช้กฎต่อไปนี้:
- protected method หรือ variable จะเริ่มต้นด้วย _
- private method หรือ variable จะพเริ่มต้นด้วย __

# Example addresses
สำหรับ url, mail address จะใช้ข้อมูลดังต่อไปนี้
example:
    * Email: someone@example.com
    * WWW: http://www.example.com
    * FTP: ftp://ftp.example.com
โดเมนเนม example.com ถูกจองเพื่อใช้เป็นตัวอย่าง (ดูที่ RFC 2606) และแนะนำว่าให้ใช้ใน
เอกสาร หรือใช้เป็นตัวอย่าง

# Files
ไฟล์ควรจะเขียนใน lower case และถ้าไฟล์มีหลายคำควรจะแบ่งด้วย underscore

# Variable types
ประเภทตัวแปรที่ใช้ใน DocBlocks:
mixed ตัวแปรที่สามารถมีได้หลายชนิด
integer ตัวเลขจำนวนเต็มทุกชนิด
float ตัวเลขจำนวนจริง
boolean ค่าตรรกกะ (true หรือ false)
string สตริง (ค่าใดๆ ใน "" หรือ '')
array อะเรย์
object ออบเจค
resource รีซอร์ซ ตัวอย่างเช่นค่าที่ return จาก mysql_connect()

# Constants
ค่าคงที่ควรจะกำหนดเป็นตัวใหญ่
<?php
define('CONSTANT', 1);
?>

ถ้าในค่าคงที่มีหลายคำควรจะแบ่งคำด้วย underscore
<?php
define('LONG_NAMED_CONSTANT', 2);
?>

Ref: http://book.cakephp.org/view/509/Coding-Standards

php coding standard

ผมอาจจะแปลผิดมั่งถูกมั่ง ถ้าอ่านแล้วไม่รู้เรื่องให้ไปอ่านต้นฉบับที่นี่ครับ :D
http://www.dagbladet.no/development/phpcodingstandard/

ชื่อ Class


- ตั้งชื่อให้ตามสิ่งที่มันเป็น
- ชื่อผสมที่มากกว่า 3 คำอาจจะทำให้สับสน, ตรวจสอบการออกแบบว่า object ของคุณมีหน้าที่รับผิดชอบมากเกินไปหรือเปล่า
- อย่าพยายามที่จะนำชื่อ class แม่ มาผสมในคลาสที่ได้รับการสืบทอด ชื่อคลาสควรจะเป็นชื่อที่อธิบายถึงตัวมันเอง
- อาจจะใช้ suffix เพื่อระบุหน้าที่ของ class

ชื่อ method และชื่อ function


- ปกติ method และ function จะทำงานบางอย่าง, ชื่อของมันจึงควรบอกได้ชัดเจนว่ามันทำอะไร
- ใช้ suffix เพื่อระบุข้อมูลที่คืนมาจาก function
--- Max - ค่าสูงสุด
--- Cnt - จำนวนข้อมูล
--- Key - ค่า key
--- ตัวอย่างเช่น pageMax, pageCnt,
- ใช้ prefix เพื่อระบุถึงหน้าที่ของ function
--- is - เพื่อบอกคำตอบของคำถาม
--- get - นำค่าออกมาใช้
--- set - นำค่าไปบันทึก
--- ตัวอย่างเช่น isEmail, setBackgroundColor

ถ้าพบคำย่อซึ่งเป็น uppercase ทั้งหมด
ให้เขียน uppercase เฉพาะตัวแรกเท่านั้น
อย่างเช่น UserID ให้เปลี่ยนเป็น UserId แทน

รูปแบบชื่อคลาส


- ใช้ uppercase เป็นตัวแบ่งคำในชื่อคลาส
- ตัวอักษรตัวแรกเป็น uppercase
- ไม่ใช่ _


รูปแบบชื่อของคลาสไลบรารี่


- ใช้แทน namepsace
- เป็นตัวอักษรสองตัวเพื่อแบ่งแยกกลุ่ม class ที่มาจากที่ต่างๆ กันเช่น คนที่ชื่อ Somchai Jaidee
SjInputValidation


รูปแบบชื่อ method


- ใช้กฎแบบเดียวกับชื่อคลาส
- ** ในบาง standard จะยกเว้นตัวอักษรตัวแรกเป็นตัวเล็ก และอาจจะ _ นำหน้าชื่อ method สำหรับ private หรือ protected method


ชื่อ member attribute


- ควรจะ (ไม่บังคับ) ขึ้นต้นด้วยตัวอักษร m เพื่อให้สามารถแยกชื่อ method กับชื่อ attribute ในกรณีที่มีชื่อซ้ำกัน
- หลังจากตัวอักษร m ให้ใช้กฎเดียวกับการตั้งชื่อ class
- ใช้ r ตามหลัง m เพื่อแทนตัวแปร reference


ชื่อ parameter ของ method


- ตัวอักษรตัวแรกควรจะเป็นตัวเล็ก
- คำต่อไปจะมีวิธีตั้งชื่อเหมือนชื่อ class


ชื่อตัวแปร


- ใช้ตัวอักษรตัวเล็กเสมอ
- ใช้ _ เป็นตัวแบ่งคำ

ชื่อ element ของ array


- ใช้ _ เป็นตัวแบ่งคำ
- ไม่ใช่ - เป็นตัวแบ่งคำ
- จะต้องมี ' ใน key ของ array element
$_POST[name] ผิด
$_POST['name'] ถูก


ตัวแปร reference และ function return reference


- ควรจะนำหน้าด้วยตัวอักษร r


ตัวแปร global


- ควรจะขึ้นต้นด้วยตัวอักษร g

constant


- เป็น uppercase หมด และแบ่งคำด้วย _


ตัวแปร static


- อาจจะขึ้นต้นด้วย s


ชื่อ function


- เป็น lower case หมด และแบ่งคำด้วย _


นโยบายการตรวจสอบค่า error ที่ return จาก function


- ตรวจสอบ error จากค่าที่ return ทุกครั้ง ยกเว้นต้องการเพิกเฉยกับ error
- แทรกการแสดงคำอธิบาย error  ทุกๆ error ที่อาจเกิดขึ้น

นโยบายการใช้ {}


if ($condtion)
{
}
และ
if ($condtion){
}
ตามความเห็นของผมถ้าความซับซ้อนสูงควรจะใช้แบบแรก
เพราะเวลาใช้ editro จะหา { } ที่คู่กันได้ง่าย
แต่แบบหลังจะดีกว่าถ้าเป็นคำสั่งง่ายๆ ทำให้จำนวนบรรทัด sourcecode ไม่ยาวมาก

นโยบายการใช้ identation/tabs และ space


-ในแต่ละ indent จะเท่ากับ space 4 ช่องในแต่ละระดับ
-อย่าใช้ tabs, ใช้ space แทน. editor ส่วนใหญ่สามารถปรับ tab ให้เป็น space
-ใช้ indent เท่าที่จำเป็นอย่ามากเกินไป


นโยบาย การใช้ parens () กับ Keywords และ Functions


- อย่าใช้ parens ถัดจาก keywords. ให้เว้นช่องว่างไว้
- ใช้ parens ติดกับชื่อฟังชั่น
- อย่าใช้ parens ใน return statements ถ้าไม่จำเป็น
    if (condition)
    {
    }
    strcmp($s, $s1);
    return 1;


อย่าเขียนคำสั่งปฎิบัติการจริงใน object constructor


ภายใน constructor จะมีเฉพาะการกำหนดค่าเริ่มต้นให้กับตัวแปรและ/หรือประมวลผลคำสั่งที่ไม่มีทางที่จะเกิดข้อผิดพลาด

   class Device
   {
      function Device()    { /* initialize and other stuff */ }
      function Open()  { return FAIL; }
   };

   $dev = new Device;
   if (FAIL == $dev->Open()) exit(1);


ทำให้ฟังค์ชั่นเรียกใช้เหมือนเดิมได้หลายครั้ง


function ไม่ควรมีตัวแปร static ที่จะทำให้ไม่สามารถใช้งานเหมือเดิม


ใช้ block ในโครงสร้าง switch


switch (){
  case 1:
  case 2:
  {

  }
  break;
}


การใช้ ?:


เพื่อให้อ่านได้ง่ายให้ นำ condition ไว้ใน parens
ถ้าเป็นไปได้ตัวทดสอบควรจะเป็น function ง่ายๆ
วาง action ของ then และ else ไว้แยกบรรทัด ยกเว้นมันสามารถเขียนให้อ่านได้ง่ายในบรรทัดเดียว


การจัดเรียงส่วนประกาศตัวแปร


- ทำให้อ่านได้ง่าย
- กลุ่มของการกำหนดค่าเริ่มต้นควรจัดให้เป็นกล่องซี่เหลี่ยม
- & ควรจะอยุ่ติดกับ type ไม่ใช่ name


ควรจะมี เพียงแค่ statement เดียวในแต่ละบรรทัด


ยกเว้น statement มีความสัมพันธ์กันมาก


method สั้นๆ


- จำนวนบรรทัดควรจะจำกัดอยู่ภายใน 1 หน้า


ให้คำอธิบาย Null Statement


- เพื่อให้แสดงถึงว่าโค้ดทำงานถูกต้อง และ statement แยกออกจาก statment ที่ตามมาชัดเจน


อย่าใช้ค่าที่ไม่ใช่ 0 มาแทนค่าที่เป็นเท็จ


เช่น
if (false != f())
ถ้า function คืนค่า -1 แทน 0 จะทำให้คำสั่งเดิมทำงานเพี้ยนไป
ดังนั้นควรใช้ return ค่าเฉพาะ 0 เท่านั้น
if (f()) แทนเพื่อกันปัญหาที่อาจจะเกิดขึ้น
และไม่ควรใช้กับเงื่อนไข boolean ที่คืนค่าตัวเลข
if (!f()) ควรเขียนเป็น if (0 == f()) แทนจะเหมาะสมกว่า
ในการตั้งชื่อ function boolean ควรจะใช้ IsValid แทน checkValid

หลีกเลี่ยงการแนบ assignment ใน statement อื่นๆ


ถ้ามันทำให้อ่านยากขึ้น ยกเว้นแต่มันช่วยให้เขียนได้ง่ายขึ้นและเป็นส่วนที่ใช้เป็นประจำ
   while ($a != ($c = getchar()))
   {
      process the character
   }


comment


คำอธิบายของบอกเรื่องราว
- อธิบายถึงระบบ, โดยคาดว่ามันจะถูก robot ดึงแปลงไปอยู่ในรูป manual.
คำอธิบายคลาสเป็นส่วนหนึ่งของเนื้อเรื่อง, คำอธิบาย ลายเซ็นของ method (ชื่อ method และจำนวน parameter)
ก็เป็นส่วนหนึ่งของเนื้อเรื่อง, argument ก็เป็นอีกส่วนหนึง และส่วน implement ก็เป็นอีกส่วนหนึ่ง
ทุกๆ ส่วนควรจะถูกนำมาแสดงเพื่อบอกข้อมูลให้กับผู้อื่น เพื่อให้ทราบว่าคุณกำลังทำอะไร และเพราะอะไรจึงทำเช่นนั้น

อธิบายถึงการตัดสินใจ
ทุกๆ จุดซึ่งคุณตัดสินใจที่จะทำอะไรให้เขียนคำอธิบายว่าคุณเลือกวิธีไหนและเพราะอะไร

ใช้ headers
ใช้ระบบดึงเอกสาร อย่างเช่น ccdoc.
ส่วนถัดไปจะอธิบายถึงการใช้ cdoc เพื่ออธิบาย class และ method
headers ถูก structure ในทางที่พวกเขาสามารถ

ทุกๆ ส่วนของโปรเจคจะมี คำอธิบายภาพรวม

Gotcha Keyword แสดงคำอธิบายที่มีลักษณะเฉพาะตัว เพื่อช่วยในการดูแลรักษาและพัฒนาระบบ
:TODO: topic
หมายความว่ามีสิ่งที่ต้องทำที่นี่อีกอย่าลืม

:BUG: [bugid] topic
หมายถึงมี bug ที่รู้ที่นี่อีก, อธิบายมันและอาจจะกำหนดค่า bug ID.

:KLUDGE:
ถ้าคุณได้บางสิ่งที่แย่ไป อธิบายว่าควรจะทำอย่างไรในครั้งถัดไป ถ้าคุณมีเวลามากกว่านี้

:TRICKY:
บอกว่าโค้ดส่วนนี้เป็นเทคนิคพิเศษ ดังนั้นอย่าพยายามเปลี่ยนหากยังไม่ได้พิจารณาขั้นตอนทำงานโดยละเอียด

:WARNING:
แจ้งให้ระวังบางสิ่ง

:PARSER:
บางครั้งกำลังมีปัญหากับการ parse อธิบายว่าตอนนี้ได้ตรวจสอบอะไรไปแล้วบ้าง เพื่อจะใช้วิธีอื่นๆ ในการหาพยายามหาจุดที่เป็นปัญหาแทน

:ATTRIBUTE: value
คุณสามารถกำหนด attribue ที่คุณสร้างขึ้นเองได้

การจัดระเบียบ
ให้ Gotcha keyword เป็นสัญลักษณ์แรกใน comment
คำอธิบายอาจจะมีหลายบรรทัดแต่บรรทัดแรกควรจะเป็นข้อสรุปที่มีความหมายสมบูรณ์ในตัวเอง
ชื่อของผู้เขียน และวันที่ควรจะเป็นส่วนหนึ่งของคำอธิบาย.
แม้ว่าระบบ repository สามารถไล่ตามได้ว่าใครเป็นคนเขียนคำอธิบายนี้แต่ก็ต้องใช้เวลาในการหาว่าใครเป็นคน
เพิ่มข้อมูลนี้เข้าไป. บ่อยครั้งที่ gotcha อยู่นานเกินไปกว่าที่มันควรจะเป้น. การแทรกวันที่ช่วยให้โปรแกรมเมอร์คนอื่น
สามารถตัดสินใจได้ว่าจะทำอย่างไรกับ comment นี้ .การแทรก comment นี้ช่วยให้เรารู้ว่าจะสามารถสอบถามข้อมูลเพิ่มได้จากใคร
   // :TODO: tmh 960810: possible performance problem
   // We should really use a hash table here but for now we'll
   // use a linear search.

   // :KLUDGE: tmh 960810: possible unsafe type cast
   // We need a cast here to recover the derived type. It should
   // probably use a virtual method or template.


ผู้อ่าน document สองกลุ่ม


- Class Users
  ต้องการข้อมูลเกี่ยวกับ class interface วิธีการใช้ method และประกาศ object ฯลฯ ซึ่งจะสามารถอ่านได้จากส่วน header ของไฟล์
  โดยไม่ต้องการข้อมูลรายละเอียดเกี่ยวกับ algorithm ที่ใช้ใน function
- Class Implementors
  ผู้ผลิต class จำเป็นต้องรู้ว่า class ถูกสร้างขึ้นอย่างไร. comment ชนิดนี้จะอยู่ใน souce file ที่สร้าง class
  จะอธิบาย algorithm และการตัดสินใจออกแบบ. และบล้อก comment ใน method ควรจะมีคำอธิบายเพิ่มขึ้นอีก

เอกสารของ directory


ทุกๆ directory ควรจะมีไฟล์ README ซึ่งอธิบาย
- จุดประสงค์ของ directory และอะไรที่มันบรรจุอยู่ข้างในมัน
- คำอธิบาย 1 บรรทัดสำหรับแต่ละไฟล์
- ผู้ที่เกี่ยวข้องกับแหล่งที่มาต่างๆ
--- directory, เอกสารออนไล์, เอกสารกระดาษ, เอกสารการออกแบบ
- อื่นๆ
เอกสารสร้างนี้จช่วยผู้ที่จะแก้ไขโค้ดไม่ต้องไล่อ่านโค้ดในทุกๆ ไฟล์เพื่อหาข้อสรุป


สถานะ open/closed


การระบุสถานะ class ว่า open หรือ close
- open หมายถึงว่า class นั้นสามารถที่จะขยายเพิ่มเติมได้
- closed หมายถึงปิดการแก้ไข แต่ไม่ได้หมายถึงปิดการ extension.
เมื่อคลาสถูกทดสอบ และตรวจสอบว่ามีประสิทธิภาพดีแล้ว, คุณไม่จำเป็นต้องแก้ไข class นั้นมากให้ extend แทน
การตั้งสถานะให้ open/closed ระบบที่ขยายขึ้นมาจะไม่แก้ไขโค้ดที่ทำงานอยู่ก่อนหน้านี้.


อย่าใช้ floating point ในที่ๆ จำเป็นต้องใช้ค่า discrete


อย่างเช่นใน loop for
อย่าใช้ == และ != กับค่า float ให้ใช้ >= และ <= แทน

ใช้ if (0) แทน comment


จะเขียนง่ายกว่า

Accessor Styles


Get/Set
   class X
   {
      function GetAge()        { return $this->mAge; }
      function SetAge($age)    { $this->mAge = $age; }
      var $mAge;
   };

Attributes as Objects
   class X
   {
      function         Age()          { return $this->mAge; }
      function         Name()         { return $this->mName; }

      var              $mAge;
      var              $mName;
   }

   $x = new X;

   // Example 1
   $age = $x->Age();
   $r_age = &$x->Age(); // Reference

   // Example 2
   $name = $x->Name();
   $r_name = &$x->Name(); // Reference


ใช้ tag  <?php ?>


tag รูปแบบอื่นอาจถูกระงับการใช้งานในบาง server


No Magic Numbers


ไม่ใช่ตัวเลข แต่กำหนดค่าตัวเลขเป็น CONSTANT แทน

ช่องว่างระหว่าง token


ในแต่ละคู่อันแรกจะแทนแบบที่ผิดอันที่สองจะแทนแบบที่ถูกต้อง

<?php

$i=0;
$i = 0;

if(( $i<2 )||( $i>5 ))
if ( ($i < 2) || ($i > 5) )

foo ( $a,$b,$c )
foo($a, $b, $c)

$i=($j<5)?$j:5
$i = ($j < 5) ? $j : 5

?>


โครงสร้างควบคุม




if ($a != 2) {
$a = 2;
} else {
$a = 7;
}

if ($a != 2) {
$a = 2;
} elseif ($a == 3) {
$a = 4;
} else {
$a = 7;
}

if (($a == $b)
&& ($b == $c)
|| (Foo::CONST == $d)
) {
$a = $d;
} elseif (($a != $b)
|| ($b != $c)
) {
$a = $c;
} else {
$a = $b;
}



Ref:
http://www.dagbladet.no/development/phpcodingstandard/
http://www.evolt.org/node/60247
http://framework.zend.com/manual/en/coding-standard.coding-style.html

การแก้ปัญหาภาษาไทยใน CakePHP

:D มีหลายจุดด้วยกันครับ เดี๋ยวจะค่อยๆ รวบรวมมาให้ได้ครบๆ
1. App.encoding
ถ้าเราตั้งไฟล์ทั้งหมดให้เป็น tis-620 หรือไม่ได้ใช้ utf-8 เราจะต้องเปลี่ยนจาก 'UTF-8' เป็น 'ISO-8859-1' ครับ เหตุผลที่ไม่เปลี่ยนเป็น TIS-620 ก็เพราะว่า php ยังไม่สนับสนุน character set TIS-620 ครับ อย่างไรก็ตามสามารถใช้ ISO-8859-1 แทนได้ เพราะว่าทั้งสองแบบนี้มีรูปแบบการจัดเก็บเป็น ANSI เหมือนกัน ใช้แทนกันได้ไม่มีปัญหาครับ

ไฟล์ config/core.php

/**
* Application wide charset encoding
*/
Configure::write('App.encoding', 'ISO-8859-1');

2. database config สำหรับตรงนี้ต้องขอบคุณคุณ ErrorMan ครับ :)

var $default = array(
'driver' => 'mysql',
'persistent' => false,
'host' => 'localhost',
'login' => 'username',
'password' => 'password',
'database' => 'database_name',
'prefix' => '',
'encoding' => 'TIS620' //ถ้าใช้ utf-8 ให้เปลี่ยนเป็น UTF8 แทนครับ :)
);

3. การใช้ ajax ใน cakephp

<?php
class DoraemonsController extends AppController {

var $name = 'Doraemons';
var $helpers = array('Html', 'Form','Ajax','Javascript');
var $components = array('RequestHandler');

function beforeFilter(){
parent::beforeFilter();
//จะต้องตั้ง debug < 2
Configure::write('debug',1);
if ($this->RequestHandler->isAjax()){
//เทียบได้กับ header('content-type:text/html;charset=tis-620');
//การใช้ respondAs จะต้องเรียกใช้ใน beforeFilter
//เพราะ respondAs จะถูกเรียกโดยอัตโนมัติโดย
//CakePHP ภายหลังคำสั่ง beforeFilter
//ถ้าเราเรียกใช้ respondAs ซ้ำ, CakePHP จะถือว่าเป็นโมฆะ
$this->RequestHandler->respondAs('html',array(
'charset'=>'tis-620'
));
}
}

function index(){
}
}
?>

XPath กับ CakePHP

น่าดีใจสำหรับผู้ใช้ CakePHP ซึ่งตอนนี้มีคำสั่งที่ช่วยให้ใช้ XPath กับ Array ได้
โดยไม่ต้องง้อ PHP Standard Function เลย อิๆ

<?php
Configure::write('debug',1);

$a=array(
array('User'=>array('id'=>1,'name'=>'somchai','salary'=>100)),
array('User'=>array('id'=>2,'name'=>'somsri','salary'=>300)),
array('User'=>array('id'=>3,'name'=>'doraemon','salary'=>250)),
array('User'=>array('id'=>4,'name'=>'nobita','salary'=>150)),
array('User'=>array('id'=>5,'name'=>'suneo','salary'=>250)),
);



$b=Set::extract($a,'/User/id'); pr($b);
/*
Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => 4
[4] => 4
)
*/


$b=Set::extract($a,'/User[id=3]'); pr($b);
/*
Array
(
[0] => Array
(
[User] => Array
(
[id] => 3
[name] => doraemon
[salary] => 250
)
)
)
*/
$b=Set::extract($a,'/User/.[id=3]'); pr($b);
/*
Array
(
[0] => Array
(
[id] => 3
[name] => doraemon
[salary] => 250
)
)
*/


$b=Set::extract($a,'/User[id>3]'); pr($b);
/*
Array
(
[0] => Array
(
[User] => Array
(
[id] => 4
[name] => nobita
[salary] => 150
)
)
[1] => Array
(
[User] => Array
(
[id] => 5
[name] => suneo
[salary] => 250
)
)
)
*/


$b=Set::extract($a,'/User[id>3]/name'); pr($b);
/*
Array
(
[0] => nobita
[1] => suneo
)
*/
$b=Set::extract($a,'/User[:last]'); pr($b);
/*
Array
(
[0] => Array
(
[User] => Array
(
[id] => 5
[name] => suneo
[salary] => 250
)
)
)
*/

?>

Zend Framework Bootstrap

สืบเนื่องจากบทความที่แล้ว http://web-programming-bookmark.blogspot.com/2010/12/zend-framework-quick-start-zend.html
ความนี้มาดูโครงสร้างของ folder กันครับ
c:/www/quick .... โปรเจค quick start
c:/www/quick/application ....เก็บ application
c:/www/quick/data .... เก็บไฟล์ data หรือ database
c:/www/quick/library .... เก็บ zend library แต่ใน quick start กำหนดไว้อีกที่นึงแล้วตรงนี้ไม่ตรงเก็บอะไร
c:/www/quick/public .... เก็บ path ที่เก็บไฟล์ที่ผู้ใช้สามารถเข้ามาดูเว็บเพจได้
c:/www/quick/scripts .... เก็บไฟล์ script ที่ใช้อื่นๆ อย่างเช่นไฟล์ที่ใช้สร้างฐานข้อมูลจากไฟล์ sql

ส่วนสำคัญที่สุดจะอยู่ที่ c:/www/quick/application เป็นโฟล์เดอร์ที่เก็บไฟล์ที่ใช้ในการสร้างเว็บเพจโดยตรงครับ

เริ่มต้นจากการเข้าถึง http://localhost/quick/public/ (controller = index, action = index)
ไฟล์ c:/www/quick/public/index.php เป็นไฟล์ที่เริ่มทำงานไฟล์แรก

<?php //public/index.php

// APPLICATION_PATH คือค่าคงที่ที่ชี้ไปยัง sub directory 'application'
// เราใช้ APPLICATION_PATH เพื่อใช้ในการอ้างถึง directory 'library' ใส่ไปใน include_path
// เพื่อให้ PHP สามารถค้นหาคลาส Zend Framework ของเรา

define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/../application/'));
set_include_path(APPLICATION_PATH . '/../library' . PATH_SEPARATOR . get_include_path());

// AUTOLOADER - ตั้งค่าการ autoload
// เป็นทริกที่ช่วยให้ ZF โหลด class ไฟล์โดยอัตโนมัติ
// ดังนั้นคุณไม่ต้องเขียนโค้ด include และ require จำนวนมาก
require_once "Zend/Loader.php";
Zend_Loader::registerAutoload();

// REQUIRE APPLICATION BOOTSTRAP: เพื่อสร้างส่วนเฉพาะเจาะจงของ applicaion
// นี้อนุญาติให้คุณสร้างสภาพแวดล้อมแบบ MVC เพื่อใช้งาน. ในภายหลังคุณสามารถใช้ไฟล์นี้อีกครั้งเพื่อการทดสอบระบบ
// บล๊อก try-catch ข้างล่างสาธิตให้ดูว่าคุณจะควบคุม bootstrap exceptions ได้อย่างไร
// ใน application นี้, ถ้าคุณนิยาม APPLICATION_ENVIRONMENT เป็นอย่างอื่นนอกจาก 'production', เราจะแสดงผล
// exception และ stack trace ที่หน้าจอเพื่อช่วยในการแก้ไขปัญหาที่เกิดขึ้น
try {
require '../application/bootstrap.php';
} catch (Exception $exception) {
echo '<html><body><center>'
. 'An exception occured while bootstrapping the application.';
if (defined('APPLICATION_ENVIRONMENT') && APPLICATION_ENVIRONMENT != 'production') {
echo '<br /><br />' . $exception->getMessage() . '<br />'
. '<div align="left">Stack Trace:'
. '<pre>' . $exception->getTraceAsString() . '</pre></div>';
}
echo '</center></body></html>';
exit(1);
}

// DISPATCH: ส่งต่อการ request โดยใช้ front controller.
// front controller เป็น singleton, และในตอนนี้ควรจะถูกสร้างขึ้นแล้ว.
// เราจะจับเอา instance และส่งต่อมัน, ซึ่งก็คือการส่งต่อให้กับ application ของคุณ
Zend_Controller_Front::getInstance()->dispatch();


ไฟล์ c:/www/quick/application/bootstrap.php
สังเกตว่าไฟล์นี้จะถูก include โดยไฟล์ /public/index.php ข้างบนครับ
หน้าที่ของไฟล์ bootstrap.php ก็คือโหลดค่า config ขึ้นมา
และนำไปใช้ในการกำหนดค่าให้กับองค์ประกอบต่างๆ ของ model, view และ controller


<?php //application/bootstrap.php

// APPLICATION CONSTANTS - กำหนดค่า คงที่ท่จะใช้ใน application นี้
// ตัวแปรนี้สามารถใช้ได้ตลอดทั้ง application, แม้แต่ในไฟล์ ini
// เราประกาศค่าคงที่ APPLICATION_PATH ที่นี่สำหรับกรณีที่ entry point ของเรา
// ไม่ใช่ index.php (ตัวอย่างเช่น ถ้ามีการเรียกใช้จาก test suite หรือ script)
defined('APPLICATION_PATH')
or define('APPLICATION_PATH', dirname(__FILE__));

defined('APPLICATION_ENVIRONMENT')
or define('APPLICATION_ENVIRONMENT', 'development');

// FRONT CONTROLLER - ดึงเอา front controller มาใช้.
// Zend_Front_Controller ถูกสร้างขึ้นโดยใช้รูปแบบ Singleton,
// ซึ่งเป็นรูปแบบการออกแบบ ที่นำมาใช้เพื่อทำให้แน่ใจว่ามีเพียงแค่ 1 instance ของ
// Zend_Front_Controller ถูกสร้างในแต่ละ request
$frontController = Zend_Controller_Front::getInstance();


// CONTROLLER DIRECTORY SETUP - ให้ front controller ชี้ไปยังไดเรคทอรี่ของ
// action controller ของคุณ
$frontController->setControllerDirectory(APPLICATION_PATH . '/controllers');

// APPLICATION ENVIRONMENT - กำหนดค่า environment ปัจจุบัน
// กำหนดค่าตัวแปรใน front controller เพื่อแสดงถึง environment ปัจจุบัน
// ปกติจะเป็นค่า development, staging, testing, production, แต่ทั้งหมด
// ขึ้นอยู่กับความจำเป็นของการจัดระเบียบโครงสร้างของเว็บของคุณ
$frontController->setParam('env', APPLICATION_ENVIRONMENT);

// LAYOUT SETUP - สร้าง layout component
// คอมโพเน้นท์ Zend_Layout จะทำหน้าที่ในการสร้างรูปแบบ view 2 ขั้น
// คำสั่งนี้เป็นการบอก component ว่าจะหา layouts scripts ได้จากที่ไหน
Zend_Layout::startMvc(APPLICATION_PATH . '/layouts/scripts');

// VIEW SETUP - กำหนดค่า properties เริ่มต้นให้กับออปเจค View
// คอมโพเน้นท์ Zend_View มีประโยชน์ในการสร้าง views. ที่นี่, เราจับเอา
// instance ของ view ที่เป็น"global" จากออปเจค layout, และระบุค่า doctype
// ที่เราต้องการใช้ -- ในกรณีนี้, เราเลือกใช้ XHTML1 Strict
$view = Zend_Layout::getMvcInstance()->getView();
$view->doctype('XHTML1_STRICT');

// CONFIGURATION - สร้างออปเจค configuration
// คอมโพเน้นท์ Zend_Config_Ini จะวิเคราะห์ไฟล์ ini, และแยกแยะค่าตามแต่ละ
// section. ในที่นี่เราจะใช้ชื่อ section สอดคล้องกับ APP's Environment
$configuration = new Zend_Config_Ini(APPLICATION_PATH . '/config/app.ini', APPLICATION_ENVIRONMENT);

// DATABASE ADAPTOR - สร้าง database adpter
// Zend_Db ถูกสร้างขึ้นอยู่ในรูปแบบ factory interface ซึ่งช่วยให้ developer
// ส่งชื่อ adapter และ parameter ต่างๆ ซึ่งจะใช้ในการสร้างออปเจค
// database adater ที่เหมาะสม. ในตัวอย่างนี้, เราจะใช้ค่าที่ถูกพบใน section 'database' ของออปเจค configuration.
$dbAdapter = Zend_Db::factory($configuration->database);

// DATABASE TABLE SETUP - สร้าง Database Table Adapter
// เนื่องจาก application ของเราจะใช้งานคอมโพเน้นท์ Zend_Db_Table,
// เราจำเป็นต้องส่งค่า default adapter ไป เพื่อให้ทุกๆ ออปเจค table สามารถนำมาใช้
// ในยามที่จะส่ง query ไปยัง db
Zend_Db_Table_Abstract::setDefaultAdapter($dbAdapter);

// REGISTRY - สร้าง register ของ application
// registry ช่วยให้ในการบันทึกออปเจคที่สำคัญของ application ในที่ๆ ปลอดภัย
// และมั่นคง (ไม่อยู่ในส่วนที่เป็น global) เพื่อดึงเอามาใช้งานในอนาคต.
// เพื่อให้ application สามารถแน่ใจได้ว่าไม่ว่าอะไรจะเกิดขึ้นใน
// ส่วนที่เป็น global, ภายใน registry ยังคงมีออปเจคที่จำเป็นต้องใช้อยู่
$registry = Zend_Registry::getInstance();
$registry->configuration = $configuration;
$registry->dbAdapter = $dbAdapter;

// CLEANUP - ลบ items จากส่วน global
// เป็นการลบตัวแปร local ของ bootstrap จากส่วน global ของ script นี้
// (และทุกๆ scripts ที่เรียก bootstrap). ซึ่งจะทำให้เกิดการบังคับให้ดึงข้อมูล
// object ผ่านทาง Registry ของ Application เท่านั้น
unset($frontController, $view, $configuration, $dbAdapter, $registry);



พอดูไฟล์ bootstrap.php จบ ให้ย้อนกลับไปดูที่ไฟล์ index.php อีกทีครับ
ในไฟล์ bootstrap.php เราได้กำหนดตำแหน่ง directory ของ controller ด้วยคำสั่ง
$frontController->setControllerDirectory(APPLICATION_PATH . '/controllers');
ส่วนในไฟล์ index.php เราจะส่งต่อการทำงานไปให้กับ instance ของ front controller ด้วยคำสั่ง dispatch(); แบบข้างล่างนี้ครับ
Zend_Controller_Front::getInstance()->dispatch();

Zend Framework Quick Start เรียนรู้ Zend Framework Application ภายในชั่วโมงเดียว

เริ่มต้นไปดาวน์โหลดไฟล์มา 2 ชุดด้วยกันครับ จาก

ไฟล์ ZendFrameworkQuickstart-20080915.zip ....(1)
http://www.zendframework.com/docs/quickstart

แล้วก็ ZF 1.7 stable จาก ...(2)
http://www.zendframework.com/download/latest

ให้สมมุติว่า c:/www คือ document root ซึ่งจริงๆ อาจจะเป็น c:/appserv/www หรือ d:/www
แล้วแต่ว่าตอนแรกตั้ง config ไว้แบบไหนนะ

แตกไฟล์ (1) ไปไว้ที่ c:/www/quick (เปลี่ยนชื่อ ZendFrameworkQuickstart เป็น quick น่ะจะได้ใช้สะดวกๆ)
แตกไฟล์ (2) ไปไว้ที่ c:/www/zend (เปลี่ยนชื่อ ZendFramework-1.7.8 เป็น zend น่ะจะได้ใช้สะดวกๆอีกเช่นกัน)

จากนั้นไปที่ php.ini
เพิ่ม include_path (สะดวกดีไม่ต้องกังวลว่า zend จะอยู่ไหน)
include_path = ".;c:/www/zend/library"
อ้อแล้วก็ที่ php.ini ต้องแก้ไข short_open_tag จาก Off เป็น On ด้วยนะ

สิ่งที่จำเป็นอีกอย่างคือ appache จะต้องตั้ง config ให้ใช้ .htaccess ได้นะครับ
ถึงแม้ว่าปกติเค้าจะแนะนำให้เราตั้งโฟลเดอร์ public เป็น document root เพื่อป้องกันการเข้าถึงไฟล์ต่างๆ
แต่ในการทดสอบถ้าตั้ง public เป็น document root จะไม่สะดวกครับ จึงจำเป็นต้องแก้ไขไฟล์ดังนี้ครับ
ในไฟล์ c:/www/quick/public/.htaccess ให้แก้ไขบรรทัดที่เขียนว่า
RewriteRule ^.*$ /index.php [NC,L]
ไปเป็น
RewriteRule ^.*$ index.php [NC,L]

จากนั้นก็ restart server แล้วทดสอบที่ http://localhost/quick/public

ซึ่งจะมีเว็บเพจขึ้นมาแสดงข้อความ

Hello, from the Zend Framework MVC!
I am the index controllers's view script.


และมีลิงค์ guestbook ครับ แต่ถ้าเราคลิ้กไปจะเกิดข้อความ error

กลับไปที่ไฟล์ php.ini อีกครั้งครับ
ค้นหาบรรทัด
;extension=php_pdo.dll
และ
;extension=php_pdo_sqlite.dll
และจัดการเอาเครื่องหมาย ; ออกไปซะ

จากนั้น restart server ใหม่
ตอนนี้ http://localhost/quick/public/guestbook ก็สามารถใช้งานได้แล้วไม่มี error ครับ
ลองไปที่ http://localhost/quick/public/guestbook/sign จะเป็นการลงชื่อ guestbook ครับ
ซึ่งมี captcha มหัศจรรย์ไม่ใช่ไฟล์รูปภาพแต่เป็น ascii เรียงกันเป็นตัวอักษรครับ o_O!
และที่มหัศจรรย์กว่านั้นคือมันโคตรเบี้ยวเลยครับ อ่านไม่ค่อยจะออก เหอๆ

มีวิธีช่วยให้อ่านง่ายขึ้นคือ copy แล้วไป paste ที่กล่องด้านล่างนี้ครับ



จบ Zend Framework Quick Start ครับ มีคนเคยเขียนแบบละเอียดแล้วล่ะครับ
ผมเลยเขียนแบบไม่ละเอียดให้ดูบ้าง 555

http://www.zendframework.com/docs/quickstart
เริ่มต้นกับ zendframework
zendframework frameworkดีๆสำหรับ php

รายชื่อ Zend Framework Components

Zend_Acl


เป็น access controll list ( ACL ) แบบขนาดย่อมแต่มีความยืดหยุ่นสูง สำหรับการกำหนดสิทธิ.
โดยทั่วไป โปรแกรมจะใช้ประโยชน์จาก ACL เพื่อป้องกัน protected objects จาก requesting objects.
ในเอกสารของ ZF จะให้
resource - แทน object ที่จะถูกควบคุมการเข้าถึง
role - จะหมายถึง object ซึ่งจะร้องขอการเข้าถึง resource

Zend_Application


ช่วยให้บรรจุ configuration เริ่มต้นของโปรแกรมได้สะดวก จัดหาทรัพยากรที่นำกลับมาใช้ได้ใหม่
คลาสที่เรียบง่ายและมีความเป็นโมดูล และช่วยในการตรวจสอบ dependency. มันเอาใจใส่กับการสร้าง
environment ของ PHP และตั้งค่าช่วยในการทำให้โปรแกรม autoloding โดย default

Zend_Amf


ช่วยในการเชื่อมต่อกับ Adobe's Action Message Format (AMF)
, เพื่อให้ Flash Player ติดต่อกับ PHP ได้.
มันจัดสรรส่วนการทำงานส่วน gateway server สำหรับควบคุมการ request จาก Flash Player ไปยัง server
และเทียบเคียง request เหล่านั้นไปยัง object และ method ของ class และ callback function ที่ผู้เขียนโปรแกรมสร้างขึ้นมาเอง
การใช้งาน AMFPHP กับ ActionScript 2.0

AMF3 sepecification มีให้อ่านได้ฟรี, ซึ่งช่วยอธิบายให้ทราบว่ามีชนิดของ message อะไรซึ่งสามารถจะส่งไปมาระหว่าง
Flash Player และ server

Zend_Captcha


CAPTCHA เป็นคำย่อของ "Completely Automated Public Turing test to tell Computers and Humans Apart";
มันถูกใช้เป็นเป็นตัวตรวจสอบความสิทธิรูปแบบหนึ่ง เพื่อให้แน่ใจว่าผู้ที่ป้อนข้อมูลเข้ามาแต่ละครั้งเป็นมนุษย์ไม่ใช้โปรแกรมส่งข้อมูลอัตโนมัติ.
โดยทั่วไป, captcha จะถูกใช้พร้อม form กรอกข้อมูลซึ่งอนุญาตให้ใช้งานโดยไม่จำเป็นต้อง login เข้าสู่ระบบสมาชิก แต่คุณต้องการป้องการการส่งข้อมูลจาก spam

โค้ดตัวอย่าง captcha ที่เป็น text ธรรมดาๆ ครับ :)
จริงๆ ควรจะใช้ร่วมกับ Zend_Form นะครับ แต่เนื่องจากผมอยากให้มันดูธรรมดาๆ อันไหนเขียนแบบธรรมดาๆ ได้ก็เขียนแบบธรรมดาๆ ไปเลย

<?php
ini_set('include_path','.'.PATH_SEPARATOR.'c:\mydoc\testweb\zend\library');
require_once 'Zend/View.php';
require_once 'Zend/Captcha/Figlet.php';
require_once 'Zend/Session.php';
$view = new Zend_View();
$captcha = new Zend_Captcha_Figlet(array(
'name' => 'code',
'wordLen' => 6,
'timeout' => 30000
));
if (isset($_POST['captcha'])){
if ($captcha->isValid($_POST['captcha'])) {
echo 'correct!';
}else{
$msg=$captcha->getMessages();
echo current($msg);
}
}
$id = $captcha->generate();
echo '<span style="font-family:fixedsys">';
echo $captcha->render($view);
echo '</span>';
?>
<form action="?" method="post">
<input type="hidden" name="captcha[id]" value="<?php echo $id;?>" id="captcha-id" />
<input type="text" name="captcha[input]" id="captcha-input" value="" />
<input type="submit" value="test">
</form>


Zend_Pdf



<?php
header("Content-type: application/pdf");
ini_set('include_path','.'.PATH_SEPARATOR.'c:\mydoc\testweb\zend\library');
require_once 'Zend/Pdf.php';

$pdf1 = new Zend_Pdf();
$pdfPage = new Zend_Pdf_Page(Zend_Pdf_Page::SIZE_A4);
$pdfPage->setFont(Zend_Pdf_Font::fontWithPath('2005_iannnnnMTV.ttf'), 36);
$pdfPage->drawText('สวัสดีครับท่าน', 50, 842-50, 'UTF-8');
$pdf1->pages[]=$pdfPage;
echo $pdf1->render();


Zend_Soap_Client


อันนี้เป็นตัวอย่างการใช้งาน Zend_Soap_Client คนที่เคยใช้ dotnet xml จะทราบว่า xml ของ dotnet มันไม่ค่อย standard เท่าไหร่
คือ ' หรือ " มันจะไม่ถูกแปลงข้อมูลไปเป็น &quot; อะไรพวกเนี้ยลืมแระ และคิดว่า soap ของ dotnet ก็คงจะไม่ standard อีกเช่นเคย
Zend Framework ก็เลยสร้าง class Zend_Soap_Client_DotNet เสริมขึ้นมาจาก Zend_Soap_Client ธรรมดาเพื่อจัดการ xml format ของ dotnet โดยเฉพาะ ครับ :)
โปรแกรมนี้เรียกใช้ webservice จากเว็บ http://www.webservicex.com ครับ โดยอันนี้จะเป็น service แปลงอัตราแลกเปลี่ยนค่าเงินบาทกับเงินยูเอสดอลล่าครับ

<?php
ini_set('include_path','.'.PATH_SEPARATOR.'c:\mydoc\testweb\zend\library');
$wsdl = 'http://www.webservicex.com/CurrencyConvertor.asmx?wsdl';
require_once 'Zend/Soap/Client/DotNet.php';
$client = new Zend_Soap_Client_DotNet($wsdl);
$result = $client->__call('ConversionRate',array(
'FromCurrency'=>'USD',
'ToCurrency'=>'THB'
));
var_dump($result);
?>

โครงสร้าง MVC ของ Zend Framework

MVC


M (Model) คือเนื้อหาที่อยู่ใน application เช่น data, web service, rss feed
V (View) คือส่วนแสดงผลที่แสดงต่อผู้ใช้
C (Controller) คือส่วนจัดการกับการ request และควบคุมการทำงาน

Controller สามารถบอกให้ View แสดงผลต่างๆ ในแบบต่างๆ
View สามารถส่งข้อมูลไปให้กับ Controller

Controller สามารถดึงเอาข้อมูลจาก Model มาใช้ในการตัดสินใจต่างๆ
และสามารถส่งข้อมูลไปให้ Model เพื่อให้บันทึกข้อมูล

View สามารถเข้าถึง Model เพื่อดึงเอาข้อมูลมาใช้ แต่ไม่สามารถส่งข้อมูลเพื่อให้ Model บันทึกข้อมูล


Front Controller


จัดการกับการ requests
ส่งต่อการควบคุมให้กับ Action Controller
คืนค่า response

Zend_Controller


เกี่ยวกับ Controller
ในการสร้าง Action Controller เราจะสร้าง class ซึ่ง extends Zend_Controller_Action
ในการตั้งชื่อ class เราจะตั้งชื่อให้ลงท้ายว่า Controller ยกตัวอย่างเช่น IndexController
ตัวอักษร _ ในชื่อ class จะแทน subdirectory อย่างเช่น Test_MyController จะหมายถึง Test/MyController.php
URL ของ DoraemonNobitaController คือ /doraemon-nobita หรือ /doraemon.nobita หรือ /doraemonIndex

เกี่ยวกับ Action
คือ method ที่ controller นำมาใช้งาน
โดยจะต้องเป็น public method ที่มีชื่อลงท้ายว่า Action เช่น indexAction(), viewAction()
URL ของ magicItemAction() คือ /doraemon-nobita/magic-item หรือ /doraemon-nobita/magic.item หรือ /doraemon-nobita/magic_item

เกี่ยวกับ Modules
คือกลุ่มของ action controllers, models และ views ที่สัมพันธ์กัน
โครงสร้าง directory จะสัมพันธ์กับโครงสร้าง application directory
ชื่อคลาสของ Controller ควรจะนำหน้าด้วยชื่อ Module
  ยกตัวอย่างเช่น Cartoon_DoraemonController.php ซึ่งจะมีโครงสร้าง directory เป็น Cartoon/controller/DoraemonController.php
ชื่อของ Module จะต้องเป็น camel case เช่นเดียวกับชื่อของ Controller

Request Object  จะบรรจุข้อมูลเกี่ยวกับการ request เอาไว้ทั้งหมด
Router จะแยกข้อมูลจากการ request เช่น url ออกเป็น token ที่แสดงถึง action, controller และ module ของการ request
Dispatcher นำเอา token ที่ได้จากการ routing มาเป็นข้อมูลในการเลือก คลาส metod ของ action controller, จากนั้นจึงสั่งให้มันทำงาน
Response Object จะบรรจุข้อมูล response ที่สมบูรณ์ และมีความสามารถในการส่งข้อมูลกลับไปให้ user

Dispatch Loop

  1. Zend_Controller_Front::getInstance()->dispatch(); จัดการกับ request ที่เข้ามา

  2. สร้าง ออปเจค request และ ออปเจค response ถ้ายังไม่ได้ถูกสร้างขึ้น

  3. ทำการ route request

  4. เข้าสู่ dispatch loop

    • ในการ dispatch action ประกอบด้วยการสร้างออปเจค controller และการเรียก method ของ controller

    • ซึ่งจะทำการ dispatch ไปเรื่อยๆ จนกว่าออปเจค request จะรายงานกลับมาว่าไม่มี action ที่จะต้อง dispatch อีก



  5. คืนค่า response



Routing
default routing ได้แก่
/controller/action/key1/value1/key2/value2
/module/controller/action/key1/value1/key2/value2

การแก้ไข routing เรียกว่า Rewrite Router
Zend_Controller_Router_Rewrite คือส่วนการทำงานของ router ปกติ
อนุญาตให้เพิ่ม named route ได้มากเท่าที่ต้องการ
named route จะช่วยให้สามารถทำงานเกี่ยวกับการ route ได้ในภายหลัง การรวมกันเป็น URL
และการวิเคราะห์หาว่ามีข้อมูลอะไรบ้างที่อยู่ใน URL
การ Route ถูกประมวลผลแบบ LIFO (route ที่นิยามทีหลังสุดจะถูกประมวลผลเป็นอันดับแรก)
Route interface ช่วยให้คุณสามารถสร้างชนิดของการ route ที่จะใช้กับ application ของคุณได้เอง


ชนิดของ Route ที่ได้เตรียมไว้ให้
static: เทียงเคียงอย่างตรงไปตรงมา, ส่งต่อการทำงานสอดคล้องกับการ default route
standard: เทียงเคียงกับส่วนประกอบใน named URL, ยืดหยุ่นและอ่านได้ง่าย แต่ว่าแต่ะละส่วนประกอบจะต้องใช้ regexp ในการเทียบเคียงจึงทำงานค่อนข้างช้า
regex: เทียบเคียงโดยใช้ PCRE, มีความยืดหยุ่นสูงและเร็วกว่าแบบ standard แต่สร้างยากกว่าแบบ standard

ลองดูตัวอย่างการ route นะครับ อันนี้เป็น standard route
เมื่อเพิ่มโค้ดนี้ลงใน c:/www/quick/application/bootstrap.php นะครับ
(ก่อนคำสั่ง unset($frontController, $view, $configuration, $dbAdapter, $registry);)
ก็จะทำให้ http://localhost/quick/public/homepage ชี้ไปที่ controller index, action index ครับผม
หรือ http://localhost/quick/public/homepage ชี้ไปที่เดียวกับ http://localhost/quick/public ครับ

// Create a router
$router = $frontController->getRouter(); // returns a rewrite router by default
$router->addRoute(
'myRouteName',
new Zend_Controller_Router_Route('homepage',
array('controller' => 'index',
'action' => 'index'))
);

Action Controllers


คือคลาสที่ extends จาก Zend_Controller_Action
ใช้กำหนด public action (method ที่ลงท้ายด้วย Action) ที่ต้องการให้ controller ควบคุม
สร้าง public method ถ้าต้องการจะนำกลับมาใช้ใหม่ หรือหากต้องการให้ทดสอบโปรแกรมได้สะดวก

ใน Action Controller เราสามารถสร้าง method เพื่อ listen และแทรกคำสั่งใน event ต่อไปนี้
init(): method ที่จะถูกเรียกเมื่อ object ของ Action Controller ถูกสร้างขึ้น
preDispatch(): method ที่จะถูกเรียกก่อนการ dispatch action
postDispatch(): method ที่จะถูกเรียกหลังการ dispatch action

Utility Methods
_forward : ใช้เพื่อย้ายไปยัง action อื่น
_redirect : ใช้เพื่อย้ายไปยัง url อื่น
render : ใช้เพื่อเปลี่ยนไปใช้ view อื่นแทน default view
__call : สามารถ override method นี้เพื่อควบคุมการทำงาน undefined action ว่าจะให้ forward ไปยัง action ไหน

ViewRenderer


เป็น action helper ตัวนึงที่ ZF จะสร้าง object ขึ้นมาให้โดยอัตโนมัติ
property view ($this->view) ของ controller คือออปเจค view
เราสามารถกำหนดค่าตัวแปรให้ view โดยใช้คำสั่ง
$this->view->varname = $varname;

View Script จะถูกนำมาแสดงผลโดยอัตโนมัติหลังจาก event postDispatch()
view script จะมีชื่อตาม controller และ action
ตัวอย่างเช่น DoraemonController::showItemsAction จะมีไฟล์ view script คือ /view/scripts/doraemon/show-items.phtml
เราสามารถใช้คำสั่ง setNoRender() เพื่อบอกให้ controler ไม่นำ view script มาแสดงผลโดยอัตโนมัติ

คำสั่งเกี่ยวกับ ViewRenderer
setView
setViewSuffix
setView(Base|Script)PathSpec()
setResponseSegment()


Plugins


plugin จะถูกเรียกใน event ของ front controller
มีการแทรก event ในทุกๆ ขบวนการหลักของ front controller
ช่วยให้สามารถสร้าง action แบบ global (แทรกคำสั่งเพิ่มเข้าไปในทุกๆ action)

event
routeStartup(): ทำงานก่อนการ route
routeShutdown(): ทำงานหลังการ route
dispatchLoopStartup(): ทำงานก่อน รอบแรกของ dispatch loop
preDispatch(): ทำงานก่อนการ dispatch action
postDispatch(): ทำงานหลัง dispatch action
dispatchLoopShutdown(): ทำงานหลังจากที่ dispatch loop วนซ้ำจบ

การสร้าง plugins
ให้สร้างคลาส extends Zend_Controller_Plugin_Abstract
เขียน override event ลงใน คลาสดังกล่าว โดยอาจจะเขียนเพียง event เดียวหรือหลาย event ก็ได้


Action Helpers


Action Helper คือคลาสที่รวมเอา function ที่นำกลับมาใช้ได้ใหม่ในหลายๆ controller

การสร้าง Action Helper
สร้างคลาส extends Zend_Controller_Action_Helper_Abstract
ส่วนสุดท้ายของชื่อคลาสคือชื่อของ helper
ตัวอย่างเช่น My_Helper_SuperMan 'SuperMan' คือชื่อของ helper
สร้าง method direct() เพื่อให้ helper ถูกเรียกราวกับว่าเป็น mehod ของ helper broker

การใช้ Action Helper เป็น Action Controller Event Listener

init(): เมื่อ action controller ถูกสร้างขึ้น
preDispatch(): ทำงานหลังจาก preDispatch() ของ front controller plugins แต่ทำงานก่อน preDispatch() ของ action controller
postDispatch(): ทำงานหลังจาก postDispatch() ของ action controller แต่ทำงานก่อน postDispatch() ของ front controller plugins

โน๊ต : event listener ของ helper จะถูก trigger ก็ต่อเมื่อได้ทำการลงทะเบียนกับ broker


Zend_View


สร้างคลาสจาก Zend_View_Interface เพื่อสร้าง template engine ของคุณเอง
default คลาส Zend_View ใช้ ภาษา PHP เป็น template language
สามารถกำหนดค่าตัวแปร view ราวกับว่ามันเป็นสมาชิกของ object อย่างเช่น $view->varname = $varname;
ใช้งานตัวแปร view ใน view script ได้จากออปเจค $this ตัวอย่างเช่น <?= $this->varname ?>


View Helper


เป็นคลาสซึ่ง ขยายความสามารถให้กับ Zend_View
ตัวอย่างเช่นช่วย นำเข้า rss feed มายังเว็บเพจ
จัดรูปแบบ text ให้อยู่ในรูปแบบ xhtml
ช่วยแสดงผลตามเงื่อนไขที่ตั้งไว้อย่างเช่นแสดงผลปุ่ม login ถ้ายังไม่ได้ login
ส่วนแสดงผลที่สามารถนำกลับมาใช้หลายๆ ครั้ง เช่นกล่อง serach

การใช้ View Helper
เรียกใช้งาน Vew Helper ราวกับว่ามันเป็น method ของออปเจค View
<?= $this->formText('username') ?>

การสร้างและใช้งาน View Helper
ชื่อคลาส My_View_Helper_Coffee จะมีชื่อ helper เป็น coffee
ลงทะเบียน helper paths กับออปเจค Zend_View

จะต้องมีชื่อ method ชื่อเดียวกับ helper ดังนี้

<?php
class My_View_Helper_Coffee
{
  function coffee(){
  }
}
?>


View Filter


ช่วยในการกรอง content ก่อนจะนำไปแสดงผลจริงๆ
เหมือนกับ View Helper คือจะต้องสร้างคลาสซึ่งมี 1 method ต่อ 1 class
ประโยชน์ของ Filter เช่นแปลง HTML เป็น PDF, JSON, ลบแท็ก script ออกไปจาก content

Zend_Model


ตัวอย่างของ Model ได้แก่ Database, Web Service, RSS Feeds, FileSystem, Images

Zend Framework ระบุถึง Model ได้อย่างไร
ZF ยังไม่ได้สร้าง Model ที่เป็นแบบทั่วไปขึ้นมา
แต่ ZF สนับสนุน Model ที่มีความเฉพาะเจาะจงตัวอย่างเช่น Zend_Db_Table, Zend_Service, Zend_Feed


reference
Ophinney_Matthew_2007-ZendCon-MVC.pdf

ปฎิทินกิจกรรม php calendar


<?php
function echoDate($y,$m,$d,$acts){
$ymd = sprintf("%04d-%02d-%02d",$y,$m,$d);
echo array_key_exists($ymd,$acts)
?'<a href="act.php?date='.$ymd.'" onclick="alert(\''.$acts[$ymd].'\');return false;">'.$d.'</a>'
:$d;
}
$y = empty($_GET['year'])?date('Y'):(int)$_GET['year'];
//สมมุติว่า $acts มาจาก database
$acts = array(
'2009-08-12'=>'activity1',
'2009-12-05'=>'activity2',
);
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>calendar of year <?php echo $y;?></title>
<style type="text/css">
.w-s {color:red;}
.w-m {color:yellow;}
.w-tu {color:pink;}
.w-w {color:green;}
.w-th {color:orange;}
.w-f {color:blue;}
.w-st {color:violet;}
</style>
</head>
<body>
<h1 align="center">calendar of year <?php echo $y;?></h1>
<table cellpadding="10" align="center">
<?php
for($m=1;$m<=12;$m++):
$num = cal_days_in_month(CAL_GREGORIAN, $m, $y);
$fd = date('w',mktime(1, 1, 1, $m, 1, $y));
$d = 0;
?>
<?php if ($m%4 == 1):?>
<tr valign="top">
<?php endif;?>
<td>

<table border="1">
<caption><?php echo $m;?></caption>
<!-- first week -->
<tr><td class="w-s">s</td><td class="w-m">m</td><td class="w-tu">t</td>
<td class="w-w">w</td><td class="w-th">t</td><td class="w-f">f</td><td class="w-st">s</td>
<tr>
<?php if ($fd>0):?>
<td colspan="<?php echo $fd;?>">&nbsp;</td>
<?php endif;?>
<?php while($fd + $d++ < 7): ?>
<td><?php echoDate($y,$m,$d,$acts);?></td>
<?php endwhile; $d--; ?>
</tr>
<!-- second to fifth week -->
<?php for($x=0;$x<31;$x++): $xx=$x%7; ?>
<?php if ($xx==0):?><tr><?php endif;?>
<td><?php $d++; echoDate($y,$m,$d,$acts);?></td>
<?php if ($xx==6 || $d==$num):?>
<?php if ($xx!=6):?>
<td colspan="<?php echo 6-$xx;?>">&nbsp;</td>
<?php endif;?>
</tr>
<?php if ($d==$num){break;} ?>
<?php endif;?>
<?php endfor;?>
</table>

</td>
<?php if ($m%4 == 0):?>
</tr>
<?php endif;?>
<?php endfor;?>
</table>

<div align="center">
<?php foreach(range(2000,2021) as $year):?>
<a href="?year=<?php echo $year;?>"><?php echo $year;?></a>
<?php endforeach;?>
</div>
</body>
</html>

วันอาทิตย์ที่ 5 ธันวาคม พ.ศ. 2553

class DbExpress


<?php
/*
คลาส DbExpress จะนำข้อมูล $_POST มาสร้าง คำสั่ง query ให้โดยอัตโนมัติครับ
ทำให้เขียนโค้ดสะดวกขึ้นครับ ตอนนี้สร้างขึ้นเฉพาะคำสั่ง insert /update / delete
ส่วนคำสั่ง select จะเขียนในเวอร์ชั่นถัดไปครับ
มีคำสั่งช่วย escape string ป้องกัน sql injection
คลาสนี้ควรจะใช้ในสภาพแวดล้อมที่ magic quotes gpc off ครับ
*/

/**
*
* Description of DbExpress
*
* @author num ( http://web-programming-bookmark.blogspot.com )
* @version 0.5a
*/
class DbExpress
{
protected static $_debug;
public static function Debug($debug=true){
self::$_debug = $debug;
}
protected static $_con;
public static function Connect($host,$user,$password,$db){
if (empty(self::$_con)){
self::$_con = mysql_connect($host,$user,$password);
mysql_query('SET NAMES UTF8',self::$_con);
mysql_select_db($db,self::$_con);
}
return self::$_con;
}
public static function Close(){
if (is_resource(self::$_con))
mysql_close(self::$_con);
}
protected $_act;
protected $_cols;
protected $_table;
protected $_where;
protected function __construct($act,$table){
$this->_act = $act;
$this->_cols = array();
$this->_table = $table;
}
protected function Where(){
$args = func_get_args();
$w =& $this->_where;
if (func_num_args()==1){
$w = 'id = '.intval($_GET['id']);
}
if (func_num_args()==2){
$w = 'id = '.intval($args[1]);
}
if (func_num_args()==3){
if (is_scalar($args[2])){
$w = $args[1].' = '.self::qstr($args[2]);
} else {
$params = array_map(array($this,'qstr'),$args[2]);
array_unshift($params, str_replace('?','%s',$args[1]));
$w = call_user_func_array('sprintf', $params);
}
}
}
public static function Insert($table){
return new DbExpress('insert',$table);
}
public static function Update($table){
$o = new DbExpress('update',$table);
$params = func_get_args();
call_user_func_array(array($o,'Where'), $params);
return $o;
}
public static function Delete($table){
$o = new DbExpress('delete',$table);
$params = func_get_args();
call_user_func_array(array($o,'Where'), $params);
return $o;
}

public static function qstr($s){
return "'".mysql_real_escape_string($s,self::$_con)."'";
}
public function Query(){
if ($this->_act === 'insert'){
$names = implode(',',array_keys($this->_cols));
$values = implode(',',array_map(array($this,'qstr'),$this->_cols));
$q = sprintf('INSERT INTO %s(%s) VALUES(%s)', $this->_table, $names, $values);
} else {
//action
if ($this->_act === 'update'){
$q = '';
foreach($this->_cols as $name=>$val)
$q.= ','.$name.'='.self::qstr($val);
$q = 'UPDATE '.$this->_table.' SET '.substr($q,1).' WHERE '.$this->_where;
}
if ($this->_act === 'delete'){
$q = 'DELETE FROM '.$this->_table.' WHERE '.$this->_where;
}
}
return $q;
}
public function Save(){
$q = $this->Query();
if (self::$_debug)
echo '<br','>',htmlspecialchars($q);
mysql_query($q,self::$_con) or die(htmlspecialchars(mysql_error()));
}
protected function __get($name){
$this->_cols[$name] = empty($_POST[$name]) ? '' : $_POST[$name];
return $this;
}

}

//ตั้งค่า timezone เป็นเวลาประเทศไทย
date_default_timezone_set('Asia/Bangkok');

//จำลองข้อมูลจาก form
$_POST['title'] = 't1';
$_POST['name'] = 'n1\'';
$_POST['detail'] = 'd1';
$_POST['create_at'] = date('Y-m-d H:i:s');
$_GET['id'] = 1;


//ตัวอย่างการใช้งาน

//ต่อ database mysql
DbExpress::Connect('localhost', 'root', '12345', 'dbname');
DbExpress::Debug(true);

//ทำการ update ข้อมูลตาราง tbcontact เฉพาะ record ที่ฟิลด์ id=$_GET['id']
DbExpress::Update('tbcontact')
->title
->name
->detail
->create_at
->Save();

//ทำการ update ข้อมูลตาราง tbcontact เฉพาะ record ที่ฟิลด์ title='test'
DbExpress::Update('tbcontact','title','test\'')
->title
->name
->detail
->create_at
->Save();

//ทำการ update ข้อมูลตาราง tbcontact ตามเงื่อนไข
DbExpress::Update('tbcontact','id between ? and ?',array(18,20))
->title
->name
->detail
->create_at
->Save();

//ทำการ insert ข้อมูลตาราง tbcontact
DbExpress::Insert('tbcontact')
->title
->name
->detail
->create_at
->Save();

//ทำการ ลบ ข้อมูลตาราง tbcontact เฉพาะ record ที่ฟิลด์ title='ffffffff'
DbExpress::Delete('tbcontact','title','ffffffff')->Save();

DbExpress::Close();
?>

วันศุกร์ที่ 3 ธันวาคม พ.ศ. 2553

run mysql_close automatically

โดยปกติ mysql connection จะถูก close ให้อัตโนมัติ
แต่ในบาง server หรือบางครั้ง php จะไม่ปิด mysql connection ให้ในทันที
อาจจะต้องรอให้หลายวินาทีกว่าจะมีการปิดให้ ดังงนั้นเราจึงควรทำการปิด mysql_close เสมอ
อย่างไรก็ตามอาจจะไม่สะดวกถ้าเขียนคำสั่ง mysql_close ในทุกหน้าเราสามารถเขียนในไฟล์ include แทนได้ดังนี้


<?php
function always_run(){
mysql_close($GLOBALS['con']);
echo 'end of request. the connection is close automatically';
}
register_shutdown_function('always_run');

//connect to mysql
$con = mysql_connect('localhost','root','12345');

//มีการเรียก always_run อัตโนมัติ ที่จุดที่ script หยุดทำงาน เพื่อทำการปิด connection ให้

วันพฤหัสบดีที่ 2 ธันวาคม พ.ศ. 2553

ตั้งค่า ให้ login sql server ได้ทั้ง windows และ sql authentication

ที่ object explorer คลิ้กขวาที่ชื่อของ server และ properties และเลือก tab security เลือก server authentication เลือก sql server and window authentication mode

บางครั้ง user อาจจะ disable ทำให้ login ไม่ได้ให้คลิ้กที่ชื่อ user
ใน security > login จากนั้นแก้ให้ enable

วันพุธที่ 1 ธันวาคม พ.ศ. 2553

xp folder default view

http://www.tech-recipes.com/rx/807/xp-change-the-default-folder-view-make-all-folder-views-the-same/