Series PHP cơ bản

1) String là array

Mình đang đào kĩ thêm về PHP, nên xin tìm hiểu dần dần, post lên cùng chia sẻ với mọi người. Mong mọi người cùng thêm mắm muối, chỉ dẫn thêm cho mình.

Hôm nay mình bắt đầu với bài “String là array” trong PHP

PHP là một ngôn ngữ lỏng lẻo (=== bốc phét là linh hoạt), vì thế có nhiều ngoại lệ khi xử lý. Bài này nói về việc string được xử lý như array.

Giả sử ta có biến sau:

$mystr = “Hello world!”;

Số character của biến trên là 12. Để đếm tổng số character của string, ta dùng hàm strlen();

  1. Giờ sử echo xem string này là array ra sao. Ví dụ:

echo $mystr[0] hoặc $mystr[0], ta đều có H.

  1. Giờ sửa string này bằng cách thêm vào một giá trị thay thế, giả sử:

$mystr{0} hoặc $mystr[0] = “K”;

  1. Giờ echo $mystr; ta sẽ thấy “Hello world!” biến thành “Kello world!”;

Giờ ta thử in ra từng character dùng for. Nếu là array ta dùng count() để đếm số elements trong array. Vì đây là string nên ta đếm bằng strlen() như đã nói ở trên.

$tongsokytu = strlen($mystr);

echo $tongsokytu."
";

for ($i=0; $i < $tongsokytu; $i++) {
echo $mystr[$i]."-";
}

Kết quả: H-e-l-l-o- -w-o-r-l-d-!-

Nếu tiếng Việt string là “dậy nhậu học” thì kết quả là: “d-�-�-�-y- -n-h-�-�-�-u- -h-�-�-�-c-”, hầm bà lằm luôn. Có gì đó sai sai :-). Mình sẽ tìm hiểu thêm chỗ này. Mời cao nhân chia sẻ.

Cảm ơn mod đã sửa title giúp mình. Mình sẽ update tại bài này luôn, không mở topic mới. Nhưng welcome mọi người chia sẻ.

Update vấn đề tiếng Việt trong PHP.

Một string/ chuỗi trong PHP có thể lớn tới 2GB. Mỗi character đại diện bởi một byte. Tiếc là PHP không hỗ trợ xử lý các character multi-byte (như Unicode) nên vì sao “Dạy nhậu học” lại bị lỗi như trên.

Một số function không hỗ trợ multibyte string như substr(), strlen(). Một số function chuyên trị Unicode như utf8_encode(), utf8_decode().

bạn thử sửa lại cái file html chèn thêm đoạn code sau trong thẻ head(do lỗi encode):

<head>
    <meta charset="UTF-8" />
</head>
2 Likes

Cảm ơn bạn đã góp ý. Bình thường với tiếng Việt mình cũng phải đặt charset như vậy để hiển thị tiếng Việt. Vừa thử với array của string thì vẫn không được.

1 Like

Tiếng việt nói riêng hay các ngôn ngữ vùng châu á nói chung, phần lớn được xếp vào hàng multi-byte.

Trong PHP (hay bất kì ngôn ngữ nào), nếu muốn đếm chính xác thì phải cấp cho nó vào hàng wide.
vậy nên trường hợp này, nếu muốn đếm đúng thì phải dùng mb_strlen.

4 Likes

Cảm ơn bác đã chia sẻ. Em thử test hai hàm thì kết quả như sau:

echo mb_strlen("Xin chào dạy nhau học",  'UTF-8'); //21
echo mb_strlen("Xin chào dạy nhau học"); //21
echo strlen('Xin chào dạy nhau học'); //26
echo strlen('chào'); //5
echo mb_strlen('chào'); //4

Trên php.net ghi “A multi-byte character is counted as 1.” nếu dùng mb_strlen();
Như vậy trong hàm mb_strlen () với tiếng Việt thì không cần truyền vào tham số thứ hai ‘UTF-8’.

Updated:

Nếu dùng hàm đế từ thì kết quả rất khác nhau giữa tiếng Việt và tiếng Anh:

echo str_word_count("Xin chào dạy nhau học"); //8
echo str_word_count('chào'); //2
echo str_word_count("Xin chao day nhau hoc"); //5
echo str_word_count('chao'); //1

Bài 2: Chuyển string sang array với hàm có sẵn của PHP

Vốn dĩ một chuỗi string trong PHP có thể coi là một array, nhưng chuỗi này chuyển sang array lại dựa vào số byte, và một cách nào đấy có thể tạm nói rằng, khi chuyển qua array thì dựa vào character.

Ví dụ “hello world” thì khi loop với for ta được h-e-l-l-o-[space]-w-o-r-l-d.

Giờ ta muốn tách chuỗi theo các nhu cầu khác thì sao? PHP có sẵn 4 hàm split(), str_split(), explode() và pre_split() để chuyển sang array.

1) split() chú ý là hàm này đã bị loại bỏ trên PHP7, vẫn dùng được trên PHP5 nhưng sẽ có warning lỗi. PHP4 thì okey.

2) str_split() sinh ra để thay thế split() với tính năng tương tự, hoạt động okey trên PHP7.

cú pháp: str_split(string,length) . Như vậy delimiter nằm phía phải, đơn vị là số.

Diễn giải: hàm str_split() sẽ tách chuỗi thành array và việc tách chuỗi này tính theo số lượng character được quy định trong delimiter. Mặc định nếu không truyền vào là 1.

Giả sử:

$str = “This is a text which will be used to do something.”;

print_r(str_split($str, 1)); // Array ( [0] => T [1] => h [2] => i [3] => s [4] => [5] => i [6] => s [7] => [8] => a [9] => [10] => t [11] => e [12] => x [13] => t [14] => [15] => w [16] => h [17] => i [18] => c [19] => h [20] => [21] => w [22] => i [23] => l [24] => l [25] => [26] => b [27] => e [28] => [29] => u [30] => s [31] => e [32] => d [33] => [34] => t [35] => o [36] => [37] => d [38] => o [39] => [40] => s [41] => o [42] => m [43] => e [44] => t [45] => h [46] => i [47] => n [48] => g [49] => .

print_r(str_split($str, 5));//Array ( [0] => This [1] => is a [2] => text [3] => which [4] => will [5] => be u [6] => sed t [7] => o do [8] => somet [9] => hing. )

3) explode() cắt string theo string.

cú pháp: explode ( string $delimiter , string $string [, int $limit ] ).

Như vậy delimiter nằm phía trái. Việc chuyển chuỗi sang array tính theo chính character, không phải theo số lượng character như làm str_split(). Mặc định tham số thứ 2 của hàm str_split() là 1 nên không cần truyền vào tham số thứ 2 nếu muốn chuyển chuỗi dựa vào 1 character, nhưng explode() bắt buộc phải có delimiter.

ví dụ 1:

$str = "This is a text which will be used to do something.";

print_r(explode("t",$str)); //Array ( [0] => This is a [1] => ex [2] => which will be used [3] => o do some [4] => hing. )  

ví dụ 2:

$str = "dạy nhau học";

print_r(explode("h", $str));//Array ( [0] => dạy n [1] => au [2] => ọc ) 

Ví dụ 3:

$str = "Dạy |sớm |dạy| nhau| học";

print_r(explode("|",$str));//Array ( [0] => Dạy [1] => sớm [2] => dạy [3] => nhau [4] => học ) 

Nhìn ví dụ 3 sẽ thấy một đặc điểm quan trọng của explode() là nó sẽ xóa chính character là delimiter sau khi convert. Mình hay dùng cái này nhất để tách dữ liệu text và chuyển vào database.

Bài này tạm chưa nói tới pre_split() của PHP. Mong mọi người góp ý.

Mình có tham khảo tại: https://dotoplay.wordpress.com/2015/08/20/differences-between-str_split-and-explode-in-php/

Câu tiếng Anh và câu kết luận của bạn không liên quan gì nhau cả. Câu tiếng Anh có nghĩa là:

Một chữ cái dạng multi-byte được đếm bằng 1.

Multi-byte characters ở đây, trong Tiếng Việt, bao gồm các chữ cái ă, â, đ, ư, ơ,… Khi đếm bằng strlen nó sẽ đếm theo byte nên kết quả là > 1, còn khi đếm bằng mb_strlen thì nó dựa vào encoding để xác định số byte của 1 chữ cái rồi dựa vào số byte đó để đếm ra số chữ cái.

Prototype của hàm mb_strlen():

int mb_strlen ( string $str [, string $encoding = mb_internal_encoding() ] )

Nếu tham số thứ 2 bạn không truyền vào UTF-8 thì nó sẽ được lấy mặc định từ kết quả của hàm mb_internal_encoding(). Mà hàm này lại lấy giá trị default từ setting default_charset hoặc (internal_encoding), giá trị mặc định của nó là UTF-8 (từ PHP 5.6), còn các version < 5.6 thì giá trị mặc định là empty. Vì thế nếu chạy với PHP 5.4 chẳng hạn mà chưa set default charset thì nó sẽ ra kết quả khác.

Không chỉ hàm mb_strlen mà hầu hết các hàm mb_...() đều có tham số string $encoding = mb_internal_encoding().
Best practice là luôn set default_charset = "..." trong php.ini, prefer là UTF-8 (default từ PHP 5.6 rồi) vì nó được support rộng rãi nhất.

Khi làm việc với tiếng Việt hay các ngôn ngữ multi-byte khác thì nên chú ý dùng các hàm mb_...(), một số hàm không có thì phải tự tìm solution khác :sweat_smile:

Hầu hết các hàm str_...() không hỗ trợ multi-byte string, trừ một số hàm trong document họ note là:

Note: This function is binary-safe.

ví dụ như hàm str_replace(), còn hàm str_word_count() thì không, tuy nhiên vẫn phải đọc kỹ document. Cả hai hàm hàm này đều không có phiên bản mb_...() nhưng str_replace() thì có thể dùng cho multi-byte string còn str_word_count() thì sẽ cho kết quả sai.

Học PHP cơ bản thì không thể không đọc document, chỉ cần gõ php.net + / + tên hàm, gõ enter là có ngay document.

5 Likes

Mình cảm ơn rất nhiều và mình rất mong có thêm nhiều hơn những comments tâm huyết như thế này. Chắc chắn là mình sẽ hiểu sâu hơn và những bạn đọc bài trong series này cũng ít nhiều có thêm kinh nghiệm.

Thực ra mình kết luận là:

Như vậy trong hàm mb_strlen () với tiếng Việt thì không cần truyền vào tham số thứ hai ‘UTF-8’.

Có thể là tại mình quan sát kết quả của hai dòng code sau:

echo mb_strlen("Xin chào dạy nhau học",  'UTF-8'); //21
echo mb_strlen("Xin chào dạy nhau học"); //21

Nhưng đúng là phải nhìn vào bản chất của vấn đề kĩ hơn, khi bạn đề cập tới cấu hình của PHP và thông số mặc định uncode utf-8 thiết lập sẵn trong hàm mb_internal_encoding().

Mình mong có thêm nhiều comment hơn từ bạn :slight_smile:

Bài 3: PHP và UTF-8

Hiện PHP chưa (thật sự) hỗ trợ Unicode ở tầng thấp, do đó, để hiển thị được chuỗi UTF-8, bắt buộc phải có thêm các thao tác xử lý bổ sung, trên web, HTML, SQL.

Bài này mình tóm lược lại về UTF8 và PHP.

UTF-8 ở chính ngôn ngữ PHP

Giả sử ta có character a, á, ư. Thử nghiệm với hai cặp hàm strlen()/mb_strlen() và strpos()/mb_strpos().

Kết quả với hàm tính lượng bytes có kết quả tương ứng như sau:

echo strlen('a'); //1
echo mb_strlen('a'); //1
echo strlen('á'); //2
echo mb_strlen('á'); //1

Ở đây có thể tạm kết luận, với các string multi-bytes (chẳng hạn theo chuẩn Unicode như tiếng Việt), số lượng bytes sẽ khác nhau giữa hàm strlen() và mb_strlen().

Kết quả với hàm tìm character có trong string:

echo strpos("mana", "a"); //1
echo strpos("mán", "á"); //1
echo mb_strpos("mana", "a"); //1

echo strpos("mưán", "á"); //3
echo mb_strpos("mưán", "á"); //2

echo strpos("mán ư", "ư"); //5
echo mb_strpos("mán ư", "ư"); //4

Hàm tìm character cũng vậy, có sự khác biệt rất lớn giữa hàm strpos() và mb_strpos().

Như vậy ta buộc phải dùng các hàm có dạng mb_* khi xử lý string Unicode, đây là các hàm chuyên trị cho Unicode. Tuy nhiên, không phải hàm xử lý chuỗi nào cũng có hàm mb_ tương ứng.

Bạn có thể thiết lập mb_internal_encoding() ở đầu mỗi file PHP và hàm mb_http_output() ngay ở vị trí PHP xuất ra dữ liệu ngoài trình duyệt.

Ngoài ra, nhiều hàm PHP xử lý string có thể có thêm tham số xác định dạng mã hóa. Chẳng hạn như htmlentities().

UTF-8 ở trên hệ điều hành

Hiện nay PHP có thể chạy ở hầu hết các hệ điều hành, gồm cả Linux và Windows. Nhưng cách PHP xử lý tên file ở mỗi hệ điều hành là có thể khác nhau, trong đó, hỗ trợ Linux tốt nhất. Chẳng hạn trên Windows, nếu dùng PHP tạo một file với mã non-ASCII, lỗi có thể xuất hiện. Trên Linux và OSX, bạn có thể mã hóa tên file dạng UTF-8, nhưng trên Windows, buộc phải dùng chuẩn ISO-8859-1.

UTF-8 khi mần ăn với MySQ

Lưu ý 1: Để chắc chắn là chuỗi trên PHP sang MYSQL lưu ở định dạng UTF-8, hãy thiết lập character và collation là utf8bm4 (theo kinh nghiệm của mình có khả năng hiển thị các kí tự “lạ” nhiều hơn UTF8 thông thường, chẳng hạn có lần mình làm về phiên âm tiếng Anh thì chỉ khi thiết lập utf8bm4, hệ thống mới hiển thị được).

Lưu ý 2: Phải thiết lập trong kết nối với mysql khi viết bằng PHP. Giờ thì chắc là toàn dân xài PDO nên mình có thể thiết lập như sau:

$db = new PDO('dblib:host=your_hostname;dbname=your_db;charset=UTF-8', $user, $pass);

Với PHP 5.3.6, tùy chọn charset chưa có nên có thể thực hiện theo tùy chọn sau:

$pdo = new PDO(
    'mysql:host=hostname;dbname=defaultDbName',
    'username',
    'password',
    array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8")
);

Với mysqli, ta buộc phải thiết lập như sau:

$conn = mysqli_connect('localhost','db_username','password','your_database_name');
 mysqli_set_charset($conn,"utf8"); 

UTF-8 trên trình duyệt

Để chắc chắn là PHP sẽ xuất UTF-8, có thể dùng hàm mb_http_output(). Trên trình duyệt thì ta thường thiết lập tùy chọn meta charset như sau, đặt trong thẻ header.

<meta charset="utf-8">

Mình có tham khảo tại:

https://phpbestpractices.org/#utf-8

http://php.net/manual/en/ref.pdo-mysql.connection.php

Hello cả nhà, tiếp bài 4.

Bài 4: Biến biến; biến-hàm và gán biến bằng tham chiếu

1) Biến của biến (variable variable), ví dụ $$a

Thông thường một biến trong PHP chỉ tới một giá trị nào đó. Ta gọi biến để gọi giá trị đó. Thông thường biến có biểu tượng $, ví dụ $a ở phía trước. Vậy $$a, hoặc $$$a là gì?

Xét code sau:

<?php
    $bar = 10;
    $foo = "bar"
?>

Kết quả xuất dữ liệu khá đặc biệt, có thể echo chính $bar hoặc $$foo.

+ nếu echo $bar;// 10

+ echo $$foo;//10

Vậy cả hai đều có giá trị là 10. Nguyên nhân là vì ta đang dùng biến của biến.

Biến của biến cho phép ta sử dụng giá trị của biến nào đó mà không cần biết về nó, nói đúng hơn là biết gián tiếp.

Theo mình thì chả biết lợi hại ở đâu, nhưng mới nhìn vào hai dấu $$ cũng hơi hắt xì hơn một tí.

Khi sử dụng $$foo, PHP sẽ (bước 1), xem giá trị của biến $foo, tiếp đến convert sang chuỗi/string, tiếp đến (bước 2) tìm đến biến trùng tên với giá trị của chính biến vừa rồi, và trả về chính giá trị của biến trùng tên này. Trong trường hợp này thì:

+ bước 1: echo $foo; //cho kết quả 'bar'.

+ bước 2: vì còn một $ nữa nên lúc này sẽ tiếp tục echo $bar. Vậy ta có 10.

Giờ ta coi thử code dưới

<?php
    $foo = "Variable!\n";
    $bar = "foo";
    $wom = "bar";
    $bat = "wom";
    print $foo;
    print $$bar;
    print $$$wom;
    print $$$$bat;
?>

Kết quả là bốn lần xuất ra “Variable!”.

Chú ý, với dạng biến của biến, ta không thể dùng cho các biến môi trường “siêu biến” (superglobals), chẳng hạn $_GET['daynhauhoc];

2. Biến của hàm (variable function)

Giả sử ta có code sau:

function daynhauhoc(){

echo "hello world dạy nhau học"; }

$a = 'daynhauhoc';

$a(); //hello world dạy nhau học

echo "<br>";

function daynhauhocdi($a){

echo "hello world dạy nhau $a";

}

$a = 'daynhauhocdi';

$a("chém gió"); //hello world dạy nhau chém gió

Vậy $a() có thể gọi chính hàm, truyền vào giá trị bình thường, nhờ vào việc giá trị của biến $a ban đầu là string giống tên hàm.

3. Biến tham chiếu, ví dụ &$a; (assignment by reference)

Thỉnh thoảng trong PHP, ta có thể thấy dạng $b = &$a;. Cái của nợ gì thế này, hết $a, $$a nhức hết cả thủ rồi.

Google sẽ thấy khái niệm tham trị và tham chiếu trong lập trình. Trong PHP, tham chiếu là khi giá trị của một biến được tham chiếu tới một biến khác. cả hai biến đều chỉ tới cùng một giá trị, chả có nào sao chép cái nào cả. Một khi được tham chiếu, chúng sẽ giữ giá trị tới khi bị xóa, chẳng hạn dùng hàm unset(). Nghe dân đồn là tốc độ xử lý chậm hơn.

Quan sát ví dụ sau:

$a = 3;

$b = &$a; // chú giữa giữa dấu & và $ ta có thể để cả một trời mênh mông space cũng hem sao.

echo $b;//3

$b = 5;

echo $a;//5

Có thể thấy, sau khi $b được gán giá trị mới là 5 thì $a cũng vậy, mang giá trị 5 luôn. Chẳng có thằng nào sao chép thằng nào.

Thường thì tham chiếu và tham trị nếu dùng trong hàm thì sẽ thú vị hơn, và theo mình cũng hơi có vẻ khó hiểu hơn, mình sẽ diễn giải thêm sau.

Mình có tham khảo tại : http://www.hackingwithphp.com/3/7/0/variable-variables

Ví dụ bổ sung bài 1. String và array

Giả sử mình có code sau:

$s = '13149';
$s[$s[1]] = $s[1]+$s[3];
print_r($s);

Kết quả sẽ là ?

Okey, đầu tiên, ta thấy biến $s là một string, vì đặt trong dấu ‘’, không phải integer. Vậy theo bài 1, nó có thể là một array với các index lần lượt: 0-1-2-3-4 tương ứng với value: 1-3-1-4-9.

Ở dòng code 2, ta thấy phía phải.
$s[1] = 3.
$s[3] = 4.

Vậy $s[1] + $s[3] = 3+ 4 = 7.

Ở phía trái, ta thấy: $s[$s[1]], tức tương đương $s[3]. Như vậy, đây là cách thay đổi giá trị của array $s với index là vị trí số 3.

Vậy tóm lại $s sẽ bằng 13179. Index ở vị trí số 3 bị thay đổi.

Giờ ta có code sau:

$a = array('dạy', 'nhau', 'học');

$a[2] = 'nhậu';

$a[3] = 'đá cầu';

print_r($a); 

Kết quả là:

Array ( [0] => dạy [1] => nhau [2] => nhậu [3] => đá cầu )

Như vậy, trong khi $a[2] thì được thay thế, còn $a[3] thì được bổ sung vào.

Kết luận thêm là: nếu ta có $biến[index] thì nếu biến cũ có số index trùng thì nó THAY THẾ, nếu không thì nó THÊM VÀO.

Hello cả nhà, mình xin tiếp tục, bài 5 basic PHP.

5. Bàn thêm về toán tử so sánh

Đầu tiên là == và ===

Hai toán tử này trả về BOOLEAN TRUE hoặc FALSE, tuy nhiên rất khác nhau. Đồng thời, việc so sánh giữa các loại biến cũng khác nhau.

  1. == là toán tử so sánh tương đương (equivalent) còn === là so sánh tương đồng. Toán tử === chỉ trả về TRUE khi hai biến so sánh giống nhau về DATA và TYPE.

Ví dụ

 $x = '12'; //string

$y = 12; //integer.

if ($x==$y){
echo "Tương đương nhé";

}else{ 
	echo "Hem tương đương"; }

if($x===$y){

echo "Tương đồng nhé";

}else{ echo "Không đương đồng hen";}

Với object: Khi so sánh tương đương (==), các biến là object được coi là tương đương (==) nếu chúng có cùng thuộc tính và giá trị, và là những instance được khởi tạo của cùng một class.

Khi so sánh tương đồng (===), các biến là object chỉ tương đồng (identical) nếu và chỉ nếu chúng chỉ tới cùng một instance của cùng một class.

Sau đây là một ít mở rộng về toán tử so sánh trong PHP, có thể tìm thấy trong bảng này.

Lưu ý toán tử spaceship <=> mới có từ bản 7.0, luôn trả về 1, 0, -1, tương đương với lớn hơn, bằng và nhỏ hơn, khi so sánh. Toán tử này có thể so sánh integer, float, string, array và object.

// Integers
echo 1 <=> 1; // 0
echo 1 <=> 2; // -1
echo 2 <=> 1; // 1
 
// Floats
echo 1.5 <=> 1.5; // 0
echo 1.5 <=> 2.5; // -1
echo 2.5 <=> 1.5; // 1
 
// Strings
echo "a" <=> "a"; // 0
echo "a" <=> "b"; // -1
echo "b" <=> "a"; // 1
 
echo "a" <=> "aa"; // -1
echo "zz" <=> "aa"; // 1
 
// Arrays
echo [] <=> []; // 0
echo [1, 2, 3] <=> [1, 2, 3]; // 0
echo [1, 2, 3] <=> []; // 1
echo [1, 2, 3] <=> [1, 2, 1]; // 1
echo [1, 2, 3] <=> [1, 2, 4]; // -1
 
// Objects
$a = (object) ["a" => "b"]; 
$b = (object) ["a" => "b"]; 
echo $a <=> $b; // 0
 
$a = (object) ["a" => "b"]; 
$b = (object) ["a" => "c"]; 
echo $a <=> $b; // -1
 
$a = (object) ["a" => "c"]; 
$b = (object) ["a" => "b"]; 
echo $a <=> $b; // 1
 
// only values are compared
$a = (object) ["a" => "b"]; 
$b = (object) ["b" => "b"]; 
echo $a <=> $b; // 1

Nguồn: http://php.net/manual/en/language.operators.comparison.php

Noted:

Nếu so sánh một integer/float (số) với một string dạng số (ví dụ $a = ’12’), mỗi string sẽ convert sang dạng số và so sánh số học.

Luật so sánh như sau:

– null sang string: chuyển null sang dạng string rỗng “”;

– bool hay null so với các loại khác: chuyển sang bool.

-array và array : array ít thành viên sẽ nhỏ hơn, nếu key trong array 1 không tìm thấy trong array 2 thì không tương đương, nếu không thì so sánh bằng giá trị.

-array so với các loại biến khác: array luôn bự hơn.

-object so với các loại biến khác: object luôn bự hơn

Một bài nho nhỏ bổ sung cho hôm nay. Mình có code sau, kết quả là?

echo (int) ((0.1 + 0.7) * 10); // ép kiểu số nguyên
echo (float) ((0.1 + 0.7) * 10); // số thực

echo (0.1 + 0.7) * 10;

Và, bùm 7, 8, 8

Vãi chưởng thật, giải thích như sau: The expression ((0.1 + 0.7) * 10) should evaluate to 8. However, the output of the expression in the script evaluates to 7 because the PHP engine stores the value of the expression internally as 7.999999 instead of 7. When the fractional value is converted into an integer, the PHP engine simply truncates the fractional part. When the value is converted to int, PHP simply truncates away the fractional part, resulting in a rather significant error (12.5%, to be exact).

Mình hiện không có câu trả lời cho việc tại sao cái cụ PHP lại lưu là 7.99999 thay vì 8.

Thêm một bài toán về so sánh trong PHP

Giả sử có code sau:

$a = array (1, 2, 3);
$b = array (1 => 2, 2 => 3, 0 => 1); 
$c = array ('a' => 1, 'b' => 2, 'c' => 3);
var_dump ($a == $b); // so sánh tương đương
var_dump ($a === $b); // so sánh tương đồng
var_dump ($a == $c); // so sánh tương đương

Kết quả sẽ là? bool(true) bool(false) bool(false)
$a và $b là tương đương vì giống nhau về value nên TRUE
$a và $b không tương đồng vì thứ tự value khác nhau nên FALSE
$a và $c không tương tương khác key nên FALSE

Xin phép cả nhà bài hôm nay mình đi hơi xa xa một tí, hi vọng là không sai sai. Tại có một bạn hỏi về -> và :: trong C++, mình thử xem trong PHP thế nào.

Bài 6 Cách truy cập biến (thuộc tính) và hàm (phương thức) từ ngoài class, dùng -> và ::

Tạm thời chưa xét tới các thuộc tính và phương thức tĩnh (với từ khóa static), ta biết rằng có 2 cách để truy xuất class sau khi khởi tạo đối tượng (dùng new class) hoặc truy cập trực tiếp dùng ::.

Xét ví dụ sau:

    class Viet{ //Khởi tạo class tên Viet, chú ý tên class là case-sensitive nên 
 //nếu đã có class vieT thì sẽ bị coi là trùng.

   // 3 thuộc tính của class
    private $name;
    public $a = "Thuộc tính a, xin chào ";
    private $b = "Thuộc tính b, hello ";

   //hàm gán tên
    function set_name($ten){

        $this->name = $this->b.$ten;
    }

    //hàm trả lại tên
    function get_name(){

        return $this->name;
    }

    
    function minhhoa1(){

        return "đây là hàm minhoa1()"; // hàm này không gọi biến có trong  class
    }

    function minhhoa2(){

        return $this->a."đây là hàm minhoa2()"; //hàm truyền biến vào
    }

}


$object = new Viet; // Khởi tạo một object mới của class

$object->set_name("Vượng Nguyễn"); // gán tên theo hàm set_name

echo $object->get_name()."<br>"; // xuất tên theo hàm get_name()

echo $object->minhhoa1()."<br>";;//kết quả xuất ra: Xin chào, đây là minhoa1() 

echo $object->minhhoa2()."<br>";;//kết quả xuất ra: Xin chào, đây là minhoa1()

echo Viet::minhhoa1()."<br>"; //kết quả xuất ra: Xin chào, đây là hàm minhoa1()

echo $object->a;

echo Viet::$a; //Fatal error: Uncaught Error: Access to undeclared static property: Viet::$a

// từ code dưới trở xuống bị lỗi.

echo Viet::minhhoa2()."<br>"; //Fatal error: Uncaught Error: Using $this when not in object context in

Viet::set_name("Việt PHP"); //Fatal error: Uncaught Error: Using $this when not in object context 

echo Viet::get_name();

Kết luận:

  1. Cách khởi tạo object thông thường, dùng new class là chuẩn rồi. Cách truy xuất tới thuộc tính và phương thức của class dùng -> là chuẩn, nếu ở đây không có thuộc tính và phương thức tĩnh. Với phương thức và thuộc tính tĩnh, không cần khởi tạo, truy xuất luôn.

  2. Hàm echo Viet::minhhoa1() có thể truy xuất tới phương thức không chứa thuộc tính của class trong bản thân hàm (dùng từ khóa $this), nhưng lại không thể truy xuất trong trường hợp Viet::minhhoa2().

  3. Không thể dùng :: truy xuất tới thuộc tính trong class, nó chỉ được dùng cho thuộc tính tĩnh.

Chắc chắn là với sự xuất hiện của đồng chí static trong thuộc tính và phương thức thì hẳn là phức tạp lên nhiều :slight_smile:

Đây là vấn đề chung trong KHMT, gọi là floating-point, bạn tham khảo thêm một số bài viết:

2 Likes

Có lẽ bạn nên quên đi cách dùng :: với method non-static thì sẽ không còn gì là phức tạp.

Khi bạn gọi theo cách này sẽ có lỗi xảy ra, tuy nhiên error nó ở level warning nên tùy vào config mà bạn có thấy error message hay không, chẳng hạn PHP 5.6 trở đi thì error level là E_DEPRECATED, còn PHP < 5.6 thì level là E_STRICT, để xem message có thể tạm thời enable tất cả levels bằng hàm error_reporting hoặc dùng hàm error_get_last() để xem. Nó đã bị marked là Deprecated thì sẽ bị loại bỏ trong tương lai gần, khi đó error level sẽ cao hơn và script của bạn sẽ bị dừng, không chạy tiếp được.

VD:

class ABC
{
    public function xyz()
    {
        echo "Oh my girl!";
    }
}

error_reporting(E_ALL);
ABC::xyz();
print_r(error_get_last());

=> Demo

Reference:

2 Likes

Em cảm ơn bác về các bổ túc rất chi tiết.

Nhân đây em có một đoạn code sau, mời anh em vào chia sẻ kết quả, tất nhiên là không chạy trên máy để kiểm tra rùi.

class XYZ{

static $mama = 1;
var $papa = 1;
}

class ABCD extends XYZ
{

const girl = 2;
static $boy = 3;
function xyz($a)
{
    $this->hello = self::girl + ABCD::girl + $a*2 + self::$boy + ABCD::$boy + parent::$mama;
   
    return $this->hello;
}


}


$me = new ABCD;
echo $me->xyz(1) + $me->papa;

Kết quả ra nhiêu ạ? Mời mọi người vào chém gió.

Cập nhật bổ sung code, kết quả sẽ khác, kết quả của code sau là bao nhiêu?

class XYZ{

    static $mama = 1;
    var $papa = 1;

    function test(){

        return $this->papa*2;
    }
}

class ABCD extends XYZ
{
    
    const girl = 2;
    static $boy = 3;
    function xyz($a)
    {
        $this->hello = self::girl + ABCD::girl + $a*2 + self::$boy + ABCD::$boy + parent::$mama + parent::test();
       
        return $this->hello;
    }


}


$me = new ABCD;
echo $me->xyz(1) + $me->papa;
class XYZ{

static $mama = 1;
var $papa = 1;
}

class ABCD extends XYZ
{

const girl = 2;
static $boy = 3;
function xyz($a)
{
    $this->hello = self::girl + ABCD::girl + $a*2 + self::$boy + ABCD::$boy + parent::$mama;
   
    return $this->hello;
}


}


$me = new ABCD;
echo $me->xyz(1) + $me->papa;

quan sát kết quả https://3v4l.org/sOZau sẽ thấy ra 14.

Tường giải: Mình tạo class cha XYZ với hai thuộc tính mama và papa, trong đó thuộc tính papa tĩnh.

Class con ABCD kế thừa XYZ, ta dùng keyword là extends để mô tả việc kế thừa class.

Trong class con mình khai tiếp các thuộc tính girl, boy. Chú ý khi ghi const girl = 2; đây là một hằng, cũng có đặc điểm như một biến static là giá trị không thay đổi.

Trong class con ABCD mình có một hàm truyền vào tham số $a. Hàm này tính toán như sau:

$this->hello = self::girl + ABCD::girl + $a*2 + self::$boy + ABCD::$boy + parent::$mama;

Như vậy biến $this->hello sẽ có kết quả tính toán bằng $a X 2 trước vì đây là toán tử ưu tiên cộng với các giá trị tham chiếu từ class cha và cả class con như sau:

  • selft:: girl và ABCD::girl đều cho kết quả như nhau, đơn giản là tham chiếu tới tham số $girl mình đã khai là hằng, bằng 2 + 2.
  • self::$boy + ABCD::$boy đều cho kết quả như nhau, tham chiếu tới $boy, bằng 3 + 3
  • parent::$mama bằng 1 vì tham chiếu tới class cha.

Tiếp đến mình khởi tạo đối tượng $me, dùng từ khóa new ABCD;

Mình sẽ xuất ra kết quả gọi hàm xyz() và truyền vào 1, cộng với kết quả tham chiếu tới tham số $papa.

Tóm lại như sau:

  1. $me->xyz(1) tương đương với

    $this->hello = self::girl + ABCD::girl + $a*2 + self::$boy + ABCD::$boy + parent::$mama;

nó lại tương đương với kết quả sau:

2 + 2 + 1*2 + 3+ 3 + 1 = 13

  1. $me->papa; tương đương với 1.

Vậy tổng là 14.

83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?