Hướng dẫn dùng the execute trong PHP

Ở bài viết này mình muốn nói về cách php chạy với command để bạn có cái nhìn tổng quan hơn về php và hình dung việc chạy script linux php cũng dễ hiểu hơn. Nghĩa là bạn sẽ dễ hình dung được script php chạy trên console mà không liên quan đến trình duyệt. 

ahihi đồ ngốc trương thanh hùng nè

đọc dữ liệu hộ bố

ahihi đồ ngốc trương thanh hùng nè

đọc dữ liệu hộ bố

ahihi đồ ngốc trương thanh hùng nè

ahihi đồ ngốc trương thanh hùng nè

hùng ebudezain

Trong bài viết này giả sử mình đang dùng windows và xampp của mình để cài web server đang cài ở ổ C:\xampp

Các câu lệnh được chuẩn bị [prepared statements] rất hữu ích để chống lại các cuộc tấn công SQL Injection.

Chuẩn bị câu lệnh và liên kết tham số

Một câu lệnh được chuẩn bị [prepared statement] là một tính năng được sử dụng để thực thi các câu lệnh SQL giống nhau [hoặc tương tự] lặp đi lặp lại với hiệu quả cao.

Một câu lệnh được chuẩn bị về cơ bản hoạt động như thế này:

  1. Chuẩn bị: Một mẫu câu lệnh SQL được tạo và gửi đến cơ sở dữ liệu. Một số giá trị nhất định không được chỉ định mà thay bằng các ký tự ? [được gọi là tham số]. Ví dụ: INSERT INTO MyGuests VALUES[?, ?, ?]
  2. Cơ sở dữ liệu sẽ phân tích cú pháp, biên dịch và thực hiện tối ưu hóa truy vấn trên mẫu câu lệnh SQL và lưu trữ kết quả biên dịch mà không thực thi nó.
  3. Thực thi: Sau đó, ứng dụng liên kết các giá trị với các tham số và cơ sở dữ liệu thực thi câu lệnh. Ứng dụng có thể thực thi câu lệnh bao nhiêu lần tùy ý với các giá trị khác nhau

So với việc thực thi trực tiếp các câu lệnh SQL, các câu lệnh được chuẩn bị có ba ưu điểm chính:

  • Các câu lệnh được chuẩn bị giúp giảm thời gian phân tích cú pháp vì việc chuẩn bị truy vấn chỉ được thực hiện một lần [mặc dù câu lệnh được thực thi nhiều lần]
  • Liên kết các tham số giúp thu nhỏ băng thông đến máy chủ vì bạn chỉ cần gửi các tham số mỗi lần truy vấn thay vì phải gửi toàn bộ truy vấn.
  • Các câu lệnh được chuẩn bị rất hữu ích để chống lại SQL Injection. Bởi vì các giá trị tham số được truyền vào sau bằng một giao thức khác, không cần phải lọc các ký tự đặc biệt. Nếu mẫu câu lệnh gốc không xuất phát từ dữ liệu đầu vào từ bên ngoài, SQL Injection không thể xảy ra.

Chuẩn bị câu lệnh trong MySQLi

Ví dụ sau sử dụng các câu lệnh được chuẩn bị và liên kết các tham số trong MySQLi:

connect_error] {
  die["Connection failed: " . $conn->connect_error];
}

// prepare and bind
$stmt = $conn->prepare["INSERT INTO MyGuests [firstname, lastname, email] VALUES [?, ?, ?]"];
$stmt->bind_param["sss", $firstname, $lastname, $email];

// set parameters and execute
$firstname = "John";
$lastname = "Doe";
$email = "john@example.com";
$stmt->execute[];

$firstname = "Mary";
$lastname = "Moe";
$email = "mary@example.com";
$stmt->execute[];

$firstname = "Julie";
$lastname = "Dooley";
$email = "julie@example.com";
$stmt->execute[];

echo "New records created successfully";

$stmt->close[];
$conn->close[];
?>
MySQLi hướng đối tượng

Như bạn thấy trong ví dụ trên có câu truy vấn SQL như sau:


"INSERT INTO MyGuests [firstname, lastname, email] VALUES [?, ?, ?]"

Trong truy vấn SQL này chúng tôi chèn một dấu hỏi [?], đây là nơi chúng tôi muốn thay thế bằng một giá trị kiểu integer, string, double hoặc BLOB.

Sau đó, chúng tôi gọi hàm bind_param[] như sau:


$stmt->bind_param["sss", $firstname, $lastname, $email];

Hàm này liên kết các tham số với truy vấn SQL và cho cơ sở dữ liệu biết các tham số là gì. Đối số "sss" liệt kê các kiểu dữ liệu của các tham số. Ký tự s nói với mysql rằng tham số là một chuỗi. Trong truy vấn chúng ta có 3 đối số kiểu string là $firstname, $lastname, $email nên chúng ta sẽ có 3 ký tự s ["sss"].

Đối số có thể là một trong bốn loại sau:

  • i - integer
  • d - double
  • s - string
  • b - BLOB

Chúng ta phải có một trong số này cho mỗi tham số.

Bằng cách nói cho mysql kiểu dữ liệu nào được yêu cầu, chúng ta đã giảm thiểu rủi ro của SQL Injection.

PDO [PHP Data Object] là các API có sẵn của PHP cho phép người lập trình có thể kết nối để làm việc với cơ sở dữ liệu. PDO không phải là một khái niệm mới, nó được giới thiệu lần đầu tiên ở phiên bản PHP 5, hiện nay đang được sử dụng rất phổ biến. PDO là các đối tượng dữ liệu trong PHP, nó sẽ chuyển tất cả dữ liệu thành đối tượng, đồng thời cũng cung cấp các phương thức để thao tác với cơ sở dữ liệu.

I. Lưu ý: Khi Sử dụng các đoạn code có khả năng lỗi cần viết vào try, catch

try
{
$db->query[ '' ];
}
catch[ PDOException $e ]
{
trigger_error[ $e->getMessage[] ];
}

II. Fetch dữ liệu

Có hai cách căn bản để lấy dữ liệu trong PDO là query và excute.
- Query: sử dụng trong các trường hợp câu truy vấn không có biến truyền vào, hoặc các biến chắc chắn không gây ra Sql Injection

$result = $db->query[ 'select SQL_CALC_FOUND_ROWS * from nv3_config LIMIT 0, 30' ];
$rs1 = $db->query[ 'SELECT FOUND_ROWS[]' ];
$all_page = $rs1->fetchColumn[ ];
print["all_page $all_page rows
"];
foreach[ $result as $row ]// lặp qua từng dòng
{

printf[ 'module: %s
config_name: %s

', $row['module'], $row['config_name'] ];

}

- Exec: sử dụng trong các trường hợp câu truy vấn không có biến truyền vào, hoặc các biến chắc chắn không gây ra Sql Injection, giá trị trả về là số dòng thực hiện.

$count = $db->exec[ "DELETE FROM nv3_config WHERE module = 'khongco'" ];
print["Deleted $count rows"];

Ngoài ra, còn có một cách tiếp cận khác nữa là sử dụng bindParam, phương thức này sử dụng trong trường hợp nếu có biến truyền vào câu truy vấn.

$config_name = 'users2';
$del = $db->prepare[ 'DELETE FROM nv3_config2 WHERE config_name=:config_name' ];
$del->bindValue[ ':config_name', $config_name ];
$del->execute[ ];

$module = 'users';
$sth = $db->prepare[ "select * from nv3_config WHERE module=:module LIMIT 0, 4" ];
$sth->bindParam[ ':module', $module ];
$sth->execute[ ];
$array = $sth->fetchAll[];
var_export[ $array ];

III. Chỉ định dữ liệu đầu ra

Cũng như cách dùng của API mysql, dữ liệu khi được lấy ra từ cơ sở dữ liệu cần phải được “đổ” [fetch] vào một array, object hoặc một class thì mới sử dụng được. PDO cũng cung cấp một số kiểu fetch cơ bản như sau:

- PDO::FETCH_ASSOC: Trả về dạng mảng [array]

- PDO::FETCH_BOTH: Trả về một mảng, key giữa tên cột và giá trị index bắt đầu từ 0
- PDO::FETCH_BOUND: Trả về TRUE và gán các giá trị của các cột trong kết quả tương ứng vào các biến bạn đã thiết lập cho nó.
- PDO::FETCH_CLASS: Trả lại một lớp [class]
- PDO::FETCH_OBJ: Trả về một đối tượng vô danh với tên tương ứng với các cột
- PDO::FETCH_NUM: [Là số 3 ] Trả về một mảng, giá trị index bắt đầu từ 0

Mặc định, NukeViet sử dụng PDO::FETCH_ASSOC.

$sql = 'SELECT * FROM ' . NV_PREFIXLANG . '_' . $module_data . ' WHERE status=1 AND id=' . $id;

$row = $db->query[ $sql ]->fetch[];

Mặc định, NukeViet sử dụng PDO::FETCH_ASSOC.

IV. Thao tác dữ liệu

1. Create [insert]

$sth = $db->prepare['INSERT INTO ' . NV_PREFIXLANG . '_' . $module_data . ' VALUES[NULL, :name, :birthday, :class]'];

$sth->bindParam[':name', $name, PDO::PARAM_STR];
$sth->bindParam[':name', $birthday, PDO::PARAM_STR];
$sth->bindParam[':class', $class, PDO::PARAM_STR];
$sth->execute[];

2. Update

$sth = $db->prepare['UPDATE ' . NV_PREFIXLANG . '_' . $module_data . ' SET name = :name, birthday = :birthday, class = :class]'];
$sth->bindParam[':name', $name, PDO::PARAM_STR];
$sth->bindParam[':name', $birthday, PDO::PARAM_STR];
$sth->bindParam[':class', $class, PDO::PARAM_STR];
$sth->execute[];

3. Delete

$sth = $db>prepare['DELETE FROM ' . NV_PREFIXLANG . '_' . $module_data . ' WHERE id = :id'];
$stmt->bindParam[':id', $id];
$stmt->execute[];

Thực thi nhiều câu lệnh
Xem ví dụ: 

$stmt = $db->prepare['INSERT INTO ' . NV_PREFIXLANG . '_' . $module_data . ' VALUES[:name]'];
$stmt->bindParam[':name', $name];
# First insertion
$name = 'Keith';
$stmt->execute[];

# Second insertion
$name = 'Steven';
$stmt->execute[];

// Max Weight
$_sql = 'SELECT max[weight] FROM ' . NV_PREFIXLANG . '_' . $module_data . ' WHERE catid=' . $catid;
$weight = $db->query[ $_sql ]->fetchColumn[];
$weight = intval[ $weight ] + 1;

// Fetch Limit
$db->sqlreset[]
  ->select[ 'COUNT[*]' ]
  ->from[ NV_PREFIXLANG . '_' . $module_data ]
  ->where[ 'status=1' ];

$all_page = $db->query[ $db->sql[] ]->fetchColumn[];

$db->select[ '*' ]
  ->order[ 'id DESC' ]
  ->limit[ $per_page ]
  ->offset[ $page ];
while[ $row = $_query->fetch[] ]
{
    //$id = $row['id'];
}

V. Các chú ý trong lập trình CSDL với NukeViet
1. Về đặt tên các bảng và cột
- Tên bảng, tên cột chỉ dùng từ  từ 2 đến 30 ký tự, chỉ dùng các ký tự a-z, 0-9 và dấu gạch dưới
- Tất cả các dữ liệu các cột cần viết thường, [Nếu có viết hoa thì kết quả trả về trong các mảng khi Fetch dữ liệu cũng là các mảng có chỉ số viết thường]

2. Về các câu truy vấn các cột
Thường chúng ta viết câu truy vấn chúng ta viết như sau

SELECT `catid`, `parentid`, `title` FROM `nv3_vi_news_cat`

Thì cần viết lại:

SELECT catid, parentid, title FROM nv3_vi_news_cat

Tức bỏ dấu ` đi để có thể chạy được trên các loại CSDL khác, Khi đó sẽ không dùng được các tên sau trong bảng và cột của CSDL:

add, all, alter, analyze, and, as, asc, before, between, bigint, binary, both, by, call, cascade, case, change, char, character, check, collate, column, comment, condition, constraint, continue, convert, create, cross, current_user, cursor, database, databases, date, day_hour, day_minute, day_second, dec, decimal, declare, default, delayed, delete, desc, describe, distinct, distinctrow, drop, dual, else, elseif, enclosed, escaped, exists, exit, explain, false, fetch, file, float4, float8, for, force, foreign, from, fulltext, get, grant, group, having, high_priority, hour_minute, hour_second, identified, if, ignore, ignore_server_ids, in, index, infile, inner, insert, int1, int2, int3, int4, int8, integer, interval, into, is, iterate, join, key, keys, kill, leading, leave, left, level, like, limit, lines, load, lock, long, loop, low_priority, master_bind, master_heartbeat_period, master_ssl_verify_server_cert, match, middleint, minute_second, mod, mode, modify, natural, no_write_to_binlog, not, null, number, numeric, on, optimize, option, optionally, or, order, outer, outfile, partition, precision, primary, privileges, procedure, public, purge, read, real, references, release, rename, repeat, replace, require, resignal, restrict, return, revoke, right, rlike, rows, schema, schemas, select, separator, session, set, share, show, signal, spatial, sql_after_gtids, sql_before_gtids, sql_big_result, sql_calc_found_rows, sql_small_result, sqlstate, ssl, start, starting, straight_join, table, terminated, then, to, trailing, trigger, true, undo, union, unique, unlock, unsigned, update, usage, use, user, using, values, varcharacter, varying, view, when, where, while, with, write, year_month, zerofill

3. Đặt bí danh cho bảng [Table Allias]
Không dùng mệnh đề  AS cho bảng  [Vẫn có thể dùng Allias cho cột được]

Thay vì viết

SELECT t1.catid, t1.parentid, t1.title FROM nv3_vi_news_cat AS t1

Hãy viết bằng

SELECT t1.catid, t1.parentid, t1.title FROM nv3_vi_news_cat t1

4. Viết câu lệnh và lấy về chỉ số tăng tự động
Trước chúng ta viết

$sql = "INSERT INTO " . NV_PREFIXLANG . "_" . $module_data . "_cat
[catid, parentid, title, titlesite, alias, description]
VALUES
[NULL, :parentid, :title, :titlesite, :alias, :description]";

Và dùng 

$sth = $db->prepare[$sql];
$sth->bindParam…..
$sth->execute[];

Thì cần viết lại theo cách

$sql = "INSERT INTO " . NV_PREFIXLANG . "_" . $module_data . "_cat
[ parentid, title, titlesite, alias, description]
VALUES
[:parentid, :title, :titlesite, :alias, :description]";
$data_insert = array[];
$data_insert['parentid'] = $parentid;
$data_insert['title'] = $title;
$data_insert['titlesite'] = $titlesite;
$data_insert['alias'] = $alias;
$data_insert['description'] = $description;
$newcatid = $db->insert_id[ $sql, 'catid', $data_insert ];

5. Khi câu lệnh có Limit cần sử dụng lớp $db để tạo câu lệnh SQL

$db->sqlreset[]
->select[ 'COUNT[*]' ]
->from[ NV_PREFIXLANG . '_' . $module_data . '_rows' ]
->where[ 'status= 1 AND inhome=1' ];

$all_page = $db->query[ $db->sql[] ]->fetchColumn[];

$db->select[ 'id, catid, listcatid, topicid, admin_id, author, sourceid, addtime, edittime, publtime, title, alias, hometext, homeimgfile, homeimgalt, homeimgthumb, allowed_rating, hitstotal, hitscm, total_rating, click_rating' ]
->order[  'id DESC' ]
->limit[ $per_page ]
->offset[ page ];

$result = $db->query[ $db->sql[] ];

Ngoài ra nếu câu lệnh SQL có sử dụng SQL_CALC_FOUND_ROWS, SELECT FOUND_ROWS[] để lấy tổng số kết quả truy vấn [các cách này chỉ chạy trên MySQL]. Thì khi đó cần viết thành 2 câu lệnh truy vấn tương tự như trên.

6. Không sử dụng REPLACE INTO trong câu lệnh SQL, thay vì đó hãy sử dụng INSERT hay update
Nguyên nhân REPLACE INTO chỉ dùng được trên MySQL, ngoài ta REPLACE INTO nếu chạy trên MySQL với Storage Engine InnoDB thực chất cũng dùng hai câu lệnh DELETE và INSERT cùng lúc

7. PDOStatement :: rowCount [] trả về số hàng bị ảnh hưởng bởi cuối cùng DELETE, INSERT, hoặc UPDATE thực hiện bởi các đối tượng PDOStatement tương ứng.

Chủ Đề