Chapterبەش 9 · Lectureوانە 9.6

Inserting and selecting data with PHPزیادکردن و هەڵبژاردنی زانیاری بە PHP

Save what a visitor types with prepared INSERT statements and hashed passwords (Lab 7's registration page, corrected), read rows back with SELECT and fetch, count them, and split long lists into pages with LIMIT and OFFSET.

ئەوەی سەردانکەر دەینووسێت بە فەرمانی INSERTـی ئامادەکراو و هاشکراو هەڵبگرە (لاپەڕەی خۆتۆمارکردنی تاقیگەی 7، ڕاستکراوە)، بە SELECT و fetch بخوێنەوە، بیانژمێرە و لیستە درێژەکان بە LIMIT و OFFSET بکە بە چەند لاپەڕەیەک.

35 min read31 خولەک خوێندنەوە · 28 stepsهەنگاو

What you will learnچی فێر دەبیت

  • Insert a record with fixed values and check the result with try/catch.
  • Insert visitor input safely with mysqli and PDO prepared statements, and store passwords with password_hash().
  • Build and fix the Lab 7 registration page.
  • Read rows with SELECT and a while loop, and explain fetch, the three fetch modes and num_rows.
  • Use LIMIT and OFFSET for pagination and compute the offset of any page.
  • بە نرخی جێگیر زیاد بکەیت و ئەنجامەکە بە ⁦try/catch⁩ بپشکنیت.
  • نێوخستەی سەردانکەر بە سەلامەتی بە mysqli و PDO زیاد بکەیت و بە ⁦password_hash()⁩ هەڵبگریت.
  • لاپەڕەی خۆتۆمارکردنی تاقیگەی 7 دروست بکەیت و چاکی بکەیت.
  • بە SELECT و بخوێنیتەوە و fetch، سێ شێوازی fetch و ⁦num_rows⁩ ڕوون بکەیتەوە.
  • LIMIT و OFFSET بۆ بەکار بهێنیت و OFFSETـی هەر لاپەڕەیەک بژمێریت.

Saving and reading data from PHPهەڵگرتن و خوێندنەوەی زانیاری لە PHPـەوە

You can now connect to MySQL. This lecture does the two jobs that almost every database page needs: saving what a visitor types with INSERT, and reading rows back with SELECT to show them on a page. At the end you will show long lists one page at a time with LIMIT and OFFSET.

ئێستا دەتوانیت پەیوەندی بە MySQLـەوە بکەیت. ئەم وانەیە ئەو دوو کارە دەکات کە نزیکەی هەموو لاپەڕەیەکی پێویستیان پێیەتی: هەڵگرتنی ئەوەی سەردانکەر دەینووسێت بە INSERT و خوێندنەوەی بە SELECT بۆ پیشاندانیان لە لاپەڕەیەکدا. لە کۆتاییدا لیستە درێژەکان لاپەڕە بە لاپەڕە پیشان دەدەیت بە LIMIT و OFFSET.

Every page starts with the connection file from Connecting PHP to MySQL. The examples use dbcon.php (database mydb, table stage_4 from phpMyAdmin) or Lab 7's connection.php (database php_mysql, table users).

هەموو لاپەڕەیەک بە فایلی پەیوەندییەکەی پەیوەندیکردنی PHP بە MySQL دەست پێ دەکات. نموونەکان dbcon.php ( mydb، stage_4 لە phpMyAdmin) یان connection.phpـی تاقیگەی 7 ( php_mysql، users) بەکار دەهێنن.

dbcon.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
// dbcon.php: the connection file used on this page's examples
try {
    $con = mysqli_connect("localhost", "root", "", "mydb");
    mysqli_set_charset($con, "utf8mb4");
} catch (mysqli_sql_exception $e) {
    error_log($e->getMessage());
    exit("Sorry, the database is not available right now.");
}

Inserting a recordزیادکردنی تۆمارێک

The simplest insert has fixed values that you write yourself — no visitor input — so a normal query is fine. The connection object has a method query() that sends SQL to the database:

سادەترین زیادکردن نرخی جێگیری هەیە کە خۆت دەیاننووسیت — بێ نێوخستەی سەردانکەر — بۆیە ئاسایی باشە. پەیوەندییەکە query()ـی هەیە کە SQL دەنێرێت بۆ :

insert.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
include "dbcon.php";   // the connection file: it creates $con

$sql = "INSERT INTO `stage_4` (`name`, `age`, `salary`)
        VALUES ('Dilan', 24, 350)";

try {
    $con->query($sql);   // the same as: mysqli_query($con, $sql);
    echo "New record created successfully. Its id is " . $con->insert_id;
} catch (mysqli_sql_exception $e) {
    error_log($e->getMessage());
    echo "Error: the record was not saved.";
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 2

The connection file creates $con.

فایلی پەیوەندییەکە $con دروست دەکات.

Lineدێڕی 4-5

Backticks mark stage_4, name, ... as names, not keywords. id is left out because AUTO_INCREMENT fills it in.

باکتیکەکان stage_4، name، ... وەک ناو دیاری دەکەن، نەک . id نەنووسراوە چونکە AUTO_INCREMENT پڕی دەکاتەوە.

Lineدێڕی 8

query() sends the SQL to the database. For an INSERT it returns true when it works.

query() SQLـەکە دەنێرێت بۆ . بۆ INSERT کاتێک سەرکەوتوو بێت true دەگەڕێنێتەوە.

Lineدێڕی 9

insert_id is a property: the id that AUTO_INCREMENT gave the new row.

insert_id : ئەو idـەی AUTO_INCREMENT داوێتی بە نوێیەکە.

Lineدێڕی 10-13

If MySQL refuses the row, PHP 8.1+ throws mysqli_sql_exception and we land here.

ئەگەر MySQL ڕەت کردەوە، ⁦PHP 8.1+⁩ mysqli_sql_exception فڕێ دەدات و دەگەینە ئێرە.

  • The backtick (`, on the slide): it tells MySQL that the word inside is a table or column name, not a keyword.
  • query(): sends your SQL to the database and returns TRUE when an INSERT worked.
  • Always check the result: know whether the row really went in, instead of guessing. In PHP 8.1+ "checking" means catching the exception.
  • باکتیک (لە سلایدەکەدا): بە MySQL دەڵێت ئەو وشەیەی لە ناویدایە ناوی یان ، نەک .
  • query(): SQLـەکەت دەنێرێت بۆ و کاتێک INSERTێک سەرکەوتوو بوو TRUE دەگەڕێنێتەوە.
  • هەمیشە ئەنجامەکە بپشکنە: بزانە ئایا بەڕاستی چووە ژوورەوە، لە جیاتی گومانکردن. لە ⁦PHP 8.1+⁩دا «پشکنین» واتە گرتنی .
Quick checkپشکنینی خێراHow do you check that an INSERT worked (PHP 8.1 or newer)?چۆن دەپشکنیت کە INSERTێک سەرکەوتوو بووە (⁦PHP 8.1⁩ یان نوێتر)؟

Inserting a recordزیادکردنی تۆمارێک

The slide's version had a hidden bugوەشانەکەی سلایدەکە هەڵەیەکی شاراوەی تێدا بوو

Inserting what a visitor types: prepared statementsزیادکردنی ئەوەی سەردانکەر دەینووسێت: ڕستە ئامادەکراوەکان

As soon as a value comes from a visitor, you cannot glue it into the SQL text. Look what happens when the course's way (VALUES ('$name', '$pass')) meets two names:

هەر کە نرخێک لە سەردانکەرەوە هات، ناتوانیت بە دەقی SQLـەکەوە بیلکێنیت. سەیر بکە چی ڕوو دەدات کاتێک ڕێگاکەی وانەکە (VALUES ('$name', '$pass')) دوو ناو وەردەگرێت:

PHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
// Why we never paste form values into SQL (run with SQLite)
$pdo = new PDO("sqlite::memory:");
$pdo->exec("CREATE TABLE log (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, password TEXT)");
$pdo->exec("INSERT INTO log (name, password) VALUES ('Karwan', 'k-secret-1')");

function unsafe_insert(PDO $pdo, string $name, string $pass): void {
    $sql = "INSERT INTO log (name, password) VALUES ('$name', '$pass')";
    echo "SQL: $sql\n";
    try { $pdo->exec($sql); echo "Result: inserted\n\n"; }
    catch (PDOException $e) { echo "Result: ERROR, the query is broken\n\n"; }
}

unsafe_insert($pdo, "Hana O'Brien", "1234");                       // an honest name with '
unsafe_insert($pdo, "x', (SELECT password FROM log WHERE name = 'Karwan')) -- ", "anything");

// The safe way: a prepared statement
$stmt = $pdo->prepare("INSERT INTO log (name, password) VALUES (?, ?)");
$stmt->execute(["Hana O'Brien", "1234"]);

foreach ($pdo->query("SELECT id, name, password FROM log") as $r) {
    echo $r["id"] . " | " . $r["name"] . " | " . $r["password"] . "\n";
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 8

The unsafe way: the values become part of the SQL text.

ڕێگا نا-سەلامەتەکە: نرخەکان دەبنە بەشێک لە دەقی SQLـەکە.

Lineدێڕی 14

An honest surname with an apostrophe: the ' ends the text early and the SQL breaks.

پاشناوێکی ئاسایی کە ئەپۆسترۆفی تێدایە: ' دەقەکە زوو تەواو دەکات و SQLـەکە تێک دەچێت.

Lineدێڕی 15

A crafted "name" changes the meaning of the SQL: it copies Karwan's stored password into a new row. This is .

«ناو»ێکی دەستکرد واتای SQLـەکە دەگۆڕێت: هەڵگیراوەکەی Karwan کۆپی دەکات بۆ نوێ. ئەمە .

Lineدێڕی 18-19

The safe way: ? marks where the values go; the values are sent separately.

ڕێگا سەلامەتەکە: ? شوێنی نرخەکان دیاری دەکات؛ نرخەکان بە جیا دەنێردرێن.

Definitionپێناسە

An SQL statement sent to the database with placeholders (? or :name) instead of values. The values are sent separately, so is never run as SQL. This is the main defence against .

فەرمانێکی SQLـە کە بە شوێنگرەوە (? یان :name) لە جیاتی نرخەکان دەنێردرێت بۆ . نرخەکان بە جیا دەنێردرێن، بۆیە هەرگیز وەک SQL جێبەجێ ناکرێت. ئەمە سەرەکیترین ڕێگای بەرگرییە دژی .

The printed deposit slipپسوولە چاپکراوەکە

A prepared statement is like a printed bank deposit slip. The bank printed the text ("Pay into account number ____ the amount ____"); you only write in the empty boxes. Whatever you write in a box — even the words "and close the account" — stays an answer in that box. It can never change the printed instructions.

وەک پسوولەیەکی چاپکراوی دانانی پارە وایە لە بانکدا. بانکەکە دەقەکەی چاپ کردووە («بخەرە سەر ژمارەی هەژماری ⁦____⁩ بڕی ⁦____⁩»)؛ تۆ تەنها خانە بەتاڵەکان پڕ دەکەیتەوە. هەرچی لە خانەیەکدا بنووسیت — تەنانەت وشەی «و هەژمارەکە دابخە» — هەر وەڵامێک دەمێنێتەوە لەو خانەیەدا. هەرگیز ناتوانێت ڕێنماییە چاپکراوەکان بگۆڕێت.

Where the analogy stops workingسنووری ئەم نموونەیە

The empty boxes can only hold values. A table name or a column name cannot be sent as a ?; choose those in your code from a fixed list.

خانە بەتاڵەکان تەنها دەتوانن نرخ هەڵبگرن. ناوی یان ناوی ناتوانرێت وەک ? بنێردرێت؛ ئەوانە لە کۆدەکەتدا لە لیستێکی جێگیرەوە هەڵبژێرە.

Quick checkپشکنینی خێراWhich line inserts a visitor's name safely?کام دێڕ ناوی سەردانکەرێک بە سەلامەتی زیاد دەکات؟

Save HTML Form Data to a MySQL Database using PHPDave Hollingworth · Englishئینگلیزی · 265K viewsبینین · 5.5K likesلایک · Partبەشی 19:50–24:06 (ofلە 24:38)

Write an INSERT with ? placeholders, see why pasting form values into SQL is dangerous, then bind the values and save the row.

بە ? دەنووسیت، دەبینیت بۆچی خستنەناوی ڕاستەوخۆی بۆ ناو مەترسیدارە، پاشان دەبەستیتەوە و پاشەکەوت دەکەیت.

Watch 19:50–24:06: INSERT with placeholders, the injection risk, bind and execute

لە ⁦19:50⁩ تا ⁦24:06⁩ سەیر بکە: بە ، مەترسیی ، پاشان بەستنەوەی و جێبەجێکردن.

Inserting what a visitor types: prepared statementsزیادکردنی ئەوەی سەردانکەر دەینووسێت: ڕستە ئامادەکراوەکان

With mysqli: prepare, bind, executeبە mysqli: ئامادەکردن، بەستنەوە، جێبەجێکردن

insert_prepared.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
include "dbcon.php";

// Imagine these three values came from a form
$name   = "Hana O'Brien";
$age    = 23;
$salary = 450;

// 1. Prepare: the SQL has ? where the values will go
$stmt = mysqli_prepare($con, "INSERT INTO stage_4 (name, age, salary) VALUES (?, ?, ?)");

// 2. Bind: "sii" = string, integer, integer (one letter per ?)
mysqli_stmt_bind_param($stmt, "sii", $name, $age, $salary);

// 3. Execute: the values travel separately from the SQL
mysqli_stmt_execute($stmt);

echo "Saved " . mysqli_stmt_affected_rows($stmt) . " row. New id: " . mysqli_insert_id($con);
mysqli_stmt_close($stmt);
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 10

Prepare: send the SQL with a ? for each value. No quotes around ?.

ئامادەکردن: SQLـەکە بنێرە لەگەڵ ? بۆ هەر نرخێک. هیچ کۆتەیشنێک بە دەوری ?دا دامەنێ.

Lineدێڕی 13

Bind: connect PHP variables to the markers. The type string has one letter per ?: s string, i integer, d decimal (float), b blob.

بەستنەوە: PHP بە نیشانەکانەوە ببەستە. زنجیرەی جۆرەکان بۆ هەر ?ێک یەک پیتی هەیە: s ، i ، d ، b blob.

Lineدێڕی 16

Execute: the values travel separately from the SQL. In PHP 8.1+ a failure throws.

جێبەجێکردن: نرخەکان جیا لە SQLـەکە دەڕۆن. لە ⁦PHP 8.1+⁩دا شکست فڕێ دەدات.

Lineدێڕی 18

mysqli_stmt_affected_rows() = rows added; mysqli_insert_id() = the new id.

mysqli_stmt_affected_rows() = ئەو زیاد کراون؛ mysqli_insert_id() = idـە نوێیەکە.

The type letters of mysqli_stmt_bind_param()پیتەکانی جۆر لە ⁦mysqli_stmt_bind_param()⁩
LetterپیتType of the valueجۆری نرخەکەExampleنموونە
s (also dates and emails) (هەروەها بەروار و )"Hana O'Brien"
i23
d (decimal)12.5
bbinary data (), sent in pieces دووانەیی ()، پارچە پارچە دەنێردرێتan image fileفایلی وێنە

Inserting what a visitor types: prepared statementsزیادکردنی ئەوەی سەردانکەر دەینووسێت: ڕستە ئامادەکراوەکان

With PDO: prepare and executeبە PDO: ئامادەکردن و جێبەجێکردن

PDO is shorter: give the values to execute() as an array. You can use ? or named markers such as :name. This one really runs (with SQLite):

PDO کورتترە: نرخەکان وەک بدە بە execute(). دەتوانیت ? یان نیشانەی ناودار وەک :name بەکار بهێنیت. ئەمەیان بەڕاستی کار دەکات (لەگەڵ SQLite):

PHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
// The same insert with PDO (shown here with SQLite so it can run anywhere)
$pdo = new PDO("sqlite::memory:");
$pdo->exec("CREATE TABLE stage_4 (id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR(100) NOT NULL, age INT NOT NULL, salary INT NOT NULL)");

$name   = "Hana O'Brien";
$age    = 23;
$salary = 450;

$stmt = $pdo->prepare("INSERT INTO stage_4 (name, age, salary) VALUES (?, ?, ?)");
$stmt->execute([$name, $age, $salary]);
echo "Saved " . $stmt->rowCount() . " row. New id: " . $pdo->lastInsertId() . "\n";

// Named markers such as :name work too
$stmt = $pdo->prepare("INSERT INTO stage_4 (name, age, salary) VALUES (:name, :age, :salary)");
$stmt->execute(["name" => "Soran", "age" => 30, "salary" => 600]);

foreach ($pdo->query("SELECT * FROM stage_4") as $row) {
    echo $row["id"] . " | " . $row["name"] . " | " . $row["age"] . " | " . $row["salary"] . "\n";
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 10-11

Prepare with ?, then execute with the values in the same order.

بە ? ئامادە بکە، پاشان بە نرخەکان بە هەمان ڕیزبەندی جێبەجێی بکە.

Lineدێڕی 15-16

Named markers: the array keys match :name, :age, :salary.

نیشانەی ناودار: کلیلەکانی لەگەڵ :name، :age، :salary دەگونجێن.

Definitionپێناسە

A mark, ? or a name such as :email, written in a where a value will go. The value is sent separately, so it can never change the SQL. A placeholder can stand only for a value, never for a table or column name.

نیشانەیەکە، ? یان ناوێک وەک :email، کە لە لە شوێنی نرخێک دەنووسرێت. نرخەکە بە جیا دەنێردرێت، بۆیە هەرگیز ناتوانێت SQLـەکە بگۆڕێت. شوێنگر تەنها دەتوانێت جێگەی نرخێک بگرێتەوە، هەرگیز جێگەی ناوی یان نا.

More attacks and defences (passwords, XSS, least privilege) are in Database security.

هێرش و بەرگریی زیاتر (، XSS، کەمترین مۆڵەت) لە ئاسایشی بنکەدراوەدان.

A form that saves what is typedفۆرمێک کە ئەوەی دەنووسرێت هەڵدەگرێت

Now join a form and an insert. The flow has five steps:

ئێستا و زیادکردنێک پێکەوە ببەستە. ڕەوتەکە پێنج هەنگاوی هەیە:

  1. The visitor typesسەردانکەر دەنووسێت

    a name and a password.

    ناوێک و .

  2. The visitor presses goسەردانکەر go دادەگرێت

    The form posts to itself.

    بۆ خۆی دەنێرێت ⁦(POST)⁩.

  3. PHP reads $_POST⁦PHP $_POST⁩ دەخوێنێتەوە

    Two values arrive.

    دوو نرخ دەگەن.

  4. The SQL runsSQLـەکە جێبەجێ دەبێت

    One new row in the table log.

    یەک نوێ لە logدا.

  5. A message is shownپەیامێک پیشان دەدرێت

    Successful, or not.

    سەرکەوتوو، یان نا.

The table log has the columns id (INT, primary key, AUTO_INCREMENT), name (VARCHAR 50) and password (VARCHAR 255). Here is the page, written safely:

log id (INT، ، ⁦AUTO_INCREMENT⁩)، name ⁦(VARCHAR 50)⁩ و password ⁦(VARCHAR 255)⁩ی هەیە. ئەمە لاپەڕەکەیە، بە سەلامەتی نووسراوە:

save.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
include "dbcon.php";
$message = "";

if (isset($_POST["save"])) {
    $name     = trim($_POST["name"]);
    $password = $_POST["password"];

    if ($name === "" || $password === "") {
        $message = "Please fill in both fields.";
    } else {
        $hash = password_hash($password, PASSWORD_DEFAULT);   // store a hash, never the password

        $stmt = mysqli_prepare($con, "INSERT INTO log (name, password) VALUES (?, ?)");
        mysqli_stmt_bind_param($stmt, "ss", $name, $hash);
        mysqli_stmt_execute($stmt);          // throws an exception if it fails
        $message = "Insert is successful.";
    }
}
?>
<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>">
    <input type="text" name="name" placeholder="your name"><br>
    <input type="password" name="password" placeholder="your password"><br>
    <input type="submit" name="save" value="go">
</form>
<p><?php echo htmlspecialchars($message); ?></p>
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 5

Only run the insert when the form was submitted (the button's name is save).

تەنها کاتێک زیادکردنەکە جێبەجێ بکە کە نێردرابێت (ناوی دوگمەکە saveـە).

Lineدێڕی 9-10

Do not save empty values.

نرخی بەتاڵ هەڵمەگرە.

Lineدێڕی 12

Never store the real password: password_hash() turns it into a 60-character hash. At login, password_verify() checks it.

هەرگیز ڕاستەقینەکە هەڵمەگرە: password_hash() دەیکات بە هاشێکی ⁦60⁩ پیتی. لە کاتی ، password_verify() دەیپشکنێت.

Lineدێڕی 14-16

A prepared statement with two string values ("ss").

بە دوو نرخی ("ss").

Lineدێڕی 21

The form posts to this same page. Note the echo; htmlspecialchars() protects against XSS through the URL.

بۆ هەمان ئەم لاپەڕەیە دەنێرێت. سەرنجی echo بدە؛ htmlspecialchars() لە XSS لە ڕێگەی URLـەوە دەپارێزێت.

Lineدێڕی 23

A password field uses type="password", so the password is not shown on screen.

type="password" بەکار دەهێنێت، بۆیە لەسەر شاشە پیشان نادرێت.

Quick checkپشکنینی خێراWhat should the password column of log contain? passwordـی log دەبێت چی تێدا بێت؟

A form that saves what is typedفۆرمێک کە ئەوەی دەنووسرێت هەڵدەگرێت

What was wrong in the slide's formچی هەڵە بوو لە فۆرمەکەی سلایدەکەدا

Lab 7: a registration page with PHP and MySQLتاقیگەی 7: لاپەڕەیەکی خۆتۆمارکردن بە PHP و MySQL

In Lab 7 you connect a PHP project to the php_mysql database and its users table (built in the phpMyAdmin lecture), and save new accounts from a registration form. The steps:

لە تاقیگەی 7دا پرۆژەیەکی PHP بە php_mysql و usersـەکەیەوە دەبەستیتەوە (کە لە وانەی phpMyAdmin دروست کرا) و هەژماری نوێ لە خۆتۆمارکردنەوە هەڵدەگریت. هەنگاوەکان:

  1. Create the database and the users table in phpMyAdmin.
  2. Create the project php_mysql with a registration form in index.php.
  3. Validate the form.
  4. Write connection.php.
  5. Read the values, protect the password and require the connection.
  6. Insert the user and report success or an error.
  7. Close the connection.
  1. و users لە phpMyAdmin دروست بکە.
  2. پرۆژەی php_mysql بە خۆتۆمارکردن لە index.php دروست بکە.
  3. پشتڕاست بکەرەوە.
  4. connection.php بنووسە.
  5. نرخەکان بخوێنەوە، بپارێزە و پەیوەندییەکە require بکە.
  6. بەکارهێنەرەکە زیاد بکە و سەرکەوتن یان هەڵە ڕاپۆرت بکە.
  7. پەیوەندییەکە دابخە.

Lab 7: a registration page with PHP and MySQLتاقیگەی 7: لاپەڕەیەکی خۆتۆمارکردن بە PHP و MySQL

The registration formفۆرمی خۆتۆمارکردن

Create a PHP project called php_mysql (in NetBeans or simply a folder in htdocs) and, inside index.php, a form that lets visitors create an account. Lab 7 gives this form; it is shown here with three fixes:

پرۆژەیەکی PHP بە ناوی php_mysql دروست بکە (لە NetBeans یان تەنها فۆڵدەرێک لە htdocs) و، لە ناو index.php، کە ڕێگە بە سەردانکەران بدات هەژمار دروست بکەن. تاقیگەی 7 ئەم دەدات؛ لێرەدا بە سێ چاککردنەوە پیشان دەدرێت:

index.php (form)HTMLRuns in the browserلە ناو تۆڕگەڕ کار دەکات
<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>">
    Username: <input type="text" name="uname" size="30"><br><br>
    Email: <input type="email" name="email" size="30"><br><br>
    Password: <input type="password" name="pass1" size="30"><br><br>
    Confirm password: <input type="password" name="pass2" size="30"><br><br>
    <input type="submit" value="Create Account" name="submit"><br>
</form>
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 1

The form sends its data back to index.php itself, with POST.

زانیارییەکانی بە POST دەنێرێتەوە بۆ خودی index.php.

Lineدێڕی 3

type="email" lets the browser check the address (the lab used type="text").

type="email" ڕێگە بە دەدات ناونیشانەکە بپشکنێت (تاقیگەکە type="text"ی بەکار هێنا).

Lineدێڕی 5

The lab labels this second password box "Username:" by mistake; it is "Confirm password".

تاقیگەکە بە هەڵە ئەم چوارچێوەی دووەمی ناو دەنێت «Username:»؛ «⁦Confirm password⁩»ـە.

Lab 7: a registration page with PHP and MySQLتاقیگەی 7: لاپەڕەیەکی خۆتۆمارکردن بە PHP و MySQL

Validating the formپشتڕاستکردنەوەی فۆرمەکە

After the submit button, PHP checks the fields one after another with an if/elseif chain. Only when everything is correct does the final else save the account:

دوای ، PHP خانەکان یەک لە دوای یەک بە زنجیرەیەکی if/elseif دەپشکنێت. تەنها کاتێک هەموو شتێک دروست بێت elseـی کۆتایی هەژمارەکە هەڵدەگرێت:

The validation chain of Lab 7 (corrected messages)زنجیرەی پشکنینی تاقیگەی 7 (پەیامە ڕاستکراوەکان)
CheckپشکنینMessageپەیام
empty($_POST["uname"])Username is required
empty($_POST["email"])Please enter your email address
!filter_var($_POST["email"], FILTER_VALIDATE_EMAIL)Please enter a valid email address (added)⁦Please enter a valid email address⁩ (زیادکراوە)
empty($_POST["pass1"])Please enter your password
empty($_POST["pass2"])Please confirm your password
$_POST["pass1"] != $_POST["pass2"]The passwords did not match, please try again

Lab 7: a registration page with PHP and MySQLتاقیگەی 7: لاپەڕەیەکی خۆتۆمارکردن بە PHP و MySQL

connection.phpفایلی ⁦connection.php⁩

Create a new PHP file named connection.php with the code that connects the project to the php_mysql database:

فایلێکی نوێی PHP بە ناوی connection.php دروست بکە بەو کۆدەی پرۆژەکە بە php_mysqlـەوە دەبەستێتەوە:

connection.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
// connection.php
try {
    $connect = mysqli_connect("localhost", "root", "", "php_mysql");
    mysqli_set_charset($connect, "utf8mb4");
} catch (mysqli_sql_exception $e) {
    error_log($e->getMessage());
    exit("Could not connect to the database.");
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 4

Connect and select php_mysql in one call. XAMPP: user root, empty password (the lab's 'root','root' is the MAMP default).

پەیوەندی و هەڵبژاردنی php_mysql بە یەک بانگکردن. XAMPP: بەکارهێنەری root، بەتاڵ ('root','root'ی تاقیگەکە بنەڕەتیی MAMPـە).

Lineدێڕی 6-9

PHP 8.1+ throws when the connection fails; catch it here.

⁦PHP 8.1+⁩ کاتێک پەیوەندییەکە شکست دەهێنێت فڕێ دەدات؛ لێرە بیگرە.

Lab 7: a registration page with PHP and MySQLتاقیگەی 7: لاپەڕەیەکی خۆتۆمارکردن بە PHP و MySQL

Reading the values and saving the accountخوێندنەوەی نرخەکان و هەڵگرتنی هەژمارەکە

Inside the final else (everything is valid), the lab does four things: read the values from the form, protect the password, connect to the database, and insert the row. Then it closes the connection.

لە ناو elseـی کۆتاییدا (هەموو شتێک دروستە)، تاقیگەکە چوار کار دەکات: نرخەکان لە دەخوێنێتەوە، دەپارێزێت، پەیوەندی بە دەکات و زیاد دەکات. پاشان پەیوەندییەکە دادەخات.

The insert part (inside the final else)بەشی زیادکردن (لە ناو else ـی کۆتاییدا)PHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
// getting values from the form inputs
$name     = $_POST["uname"];
$email    = $_POST["email"];
$password = password_hash($_POST["pass1"], PASSWORD_DEFAULT);  // hash the password

require "./connection.php";   // connect to the database

try {
    $stmt = mysqli_prepare($connect, "INSERT INTO users (name, email, password) VALUES (?, ?, ?)");
    mysqli_stmt_bind_param($stmt, "sss", $name, $email, $password);
    mysqli_stmt_execute($stmt);
    echo "Account created successfully, you can log in now";
} catch (mysqli_sql_exception $e) {
    if ($e->getCode() == 1062) {   // 1062 = this value already exists in a UNIQUE column
        echo "This email address already has an account";
    } else {
        error_log($e->getMessage());
        echo "An error occurred, please try again";
    }
}
// close the connection
mysqli_close($connect);
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 2-4

Read the three values. The password is hashed with password_hash() — never stored as typed.

سێ نرخەکە بخوێنەوە. بە password_hash() هاش دەکرێت — هەرگیز وەک خۆی هەڵناگیرێت.

Lineدێڕی 6

require the connection only now, when it is needed (it could also be at the top of the page).

تەنها ئێستا، کاتێک پێویستە، پەیوەندییەکە require بکە (دەشکرا لە سەرەتای لاپەڕەکەدا بێت).

Lineدێڕی 9-11

A prepared INSERT with three strings. The column is password — make sure the table does not say passwrod (see the table lecture).

INSERTێکی ئامادەکراو بە سێ . passwordـە — دڵنیابە کە passwrod نەنووسیبێت (وانەی خشتەکە ببینە).

Lineدێڕی 13-19

The email column is UNIQUE, so a second account with the same email throws error 1062 (Duplicate entry).

email UNIQUE ـە، بۆیە هەژمارێکی دووەم بە هەمان هەڵەی ⁦1062⁩ (⁦Duplicate entry⁩) فڕێ دەدات.

Lineدێڕی 22

The final step: close the connection that was opened before.

دوا هەنگاو: ئەو پەیوەندییەی پێشتر کرابووەوە دابخە.

Quick checkپشکنینی خێراWhy does Lab 7's sha1($password) need to change?بۆچی sha1($password)ی تاقیگەی 7 پێویستی بە گۆڕین هەیە؟

Three serious problems in the lab's versionسێ کێشەی گەورە لە وەشانەکەی تاقیگەکەدا

Lab 7: a registration page with PHP and MySQLتاقیگەی 7: لاپەڕەیەکی خۆتۆمارکردن بە PHP و MySQL

The complete index.php⁦index.php⁩ـی تەواو

All the code inside index.php: the form, the PHP block after the submit button (so messages appear under the form), the validation chain and the insert.

هەموو کۆدی ناو index.php: ، بلۆکی PHP دوای (بۆ ئەوەی پەیامەکان لە ژێر دەربکەون)، زنجیرەی پشکنین و زیادکردنەکە.

index.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Create an account</title>
</head>
<body>
<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>">
    Username: <input type="text" name="uname" size="30"><br><br>
    Email: <input type="email" name="email" size="30"><br><br>
    Password: <input type="password" name="pass1" size="30"><br><br>
    Confirm password: <input type="password" name="pass2" size="30"><br><br>
    <input type="submit" value="Create Account" name="submit"><br>
<?php
if (isset($_POST["submit"])) {
    if (empty($_POST["uname"])) {
        echo "Username is required";
    } elseif (empty($_POST["email"])) {
        echo "Please enter your email address";
    } elseif (!filter_var($_POST["email"], FILTER_VALIDATE_EMAIL)) {
        echo "Please enter a valid email address";
    } elseif (empty($_POST["pass1"])) {
        echo "Please enter your password";
    } elseif (empty($_POST["pass2"])) {
        echo "Please confirm your password";
    } elseif ($_POST["pass1"] != $_POST["pass2"]) {
        echo "The passwords did not match, please try again";
    } else {
        // getting values from the form inputs
        $name     = $_POST["uname"];
        $email    = $_POST["email"];
        $password = password_hash($_POST["pass1"], PASSWORD_DEFAULT);  // hash the password

        require "./connection.php";   // connect to the database

        try {
            $stmt = mysqli_prepare($connect, "INSERT INTO users (name, email, password) VALUES (?, ?, ?)");
            mysqli_stmt_bind_param($stmt, "sss", $name, $email, $password);
            mysqli_stmt_execute($stmt);
            echo "Account created successfully, you can log in now";
        } catch (mysqli_sql_exception $e) {
            if ($e->getCode() == 1062) {   // 1062 = this value already exists in a UNIQUE column
                echo "This email address already has an account";
            } else {
                error_log($e->getMessage());
                echo "An error occurred, please try again";
            }
        }
        // close the connection
        mysqli_close($connect);
    }
}
?>
</form>
</body>
</html>
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 8-13

The corrected form.

ڕاستکراوە.

Lineدێڕی 14-15

The PHP block sits inside the form, after the button, and runs only after a submit.

بلۆکی PHP لە ناو ، دوای دوگمەکە و تەنها دوای ناردن جێبەجێ دەبێت.

Lineدێڕی 16-27

The validation chain; the first problem found is reported.

زنجیرەی پشکنین؛ یەکەم کێشە کە دۆزرایەوە ڕاپۆرت دەکرێت.

Lineدێڕی 28-51

Everything is valid: hash, connect, prepared INSERT, handle a duplicate email, close.

هەموو شتێک دروستە: هاش، پەیوەندی، INSERTـی ئامادەکراو، چارەسەرکردنی دووبارە، داخستن.

Lineدێڕی 54-56

Unlike the lab, the page ends properly with </body></html>.

بە پێچەوانەی تاقیگەکە، لاپەڕەکە بە دروستی بە </body></html> کۆتایی دێت.

A classmate's registration code: find the bugsکۆدی خۆتۆمارکردنی هاوپۆلێک: هەڵەکان بدۆزەرەوە

A student wrote another registration page for a table users (email, password). It connects, checks the fields, and saves the account. It looks reasonable but has five problems:

خوێندکارێک لاپەڕەیەکی تری خۆتۆمارکردنی بۆ users (email, password) نووسی. پەیوەندی دەکات، خانەکان دەپشکنێت و هەژمارەکە هەڵدەگرێت. ڕێک دیارە بەڵام پێنج کێشەی هەیە:

The student's code (from the practical notes) — do not copyکۆدی خوێندکارەکە (لە تێبینییە پراکتیکییەکانەوە) — کۆپی مەکەPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
$connect = mysqli_connect('localhost','root','','ABCD');
if(!$connect){
    die ("failed your database".mysqli_error($connect));
}
$emailError = null;
$PassError = null;
$RpassError = null;

if (isset($_POST['subbtn'])){
    if (empty($_POST['email'])){
        $emailError = "Require your email";
    }
    elseif (!strstr($_POST['email'],"@") && (!strstr($_POST['email'],"."))){
        $emailError = " contain @ and .";
    }
    if (empty($_POST['pass'])){
        $PassError = "require your password";
    }
    if ($_POST['rpass'] != $_POST['pass']){
        $RpassError = "Mismatch your password";
    }
}
if(!empty($_POST['email']) && !empty($_POST['pass'])){
    $email = $_POST['email'];
    $password = md5($_POST['pass']);
    $sql_command = "insert into users (email,password) values ('$email' ,'$password') ";
    if (mysqli_query($connect,$sql_command)){
        echo "done";
    }else{
        echo"Failed".mysqli_error($connect);
    }
}

Reading rowsخوێندنەوەی تۆمارەکان

Reading is steps 3 and 4 of the five steps: run a SELECT, then use the returned rows.

خوێندنەوە هەنگاوی 3 و 4ی پێنج هەنگاوەکەیە: SELECTێک جێبەجێ بکە، پاشان گەڕاوەکان بەکار بهێنە.

Reading rowsخوێندنەوەی تۆمارەکان

SELECT and a while loopSELECT و خولێکی while

To show rows, run a SELECT, then take the rows one at a time in a while loop and print each one. Here the users of Lab 7 are shown in an :

بۆ پیشاندانی ، SELECTێک جێبەجێ بکە، پاشان لە یەک یەک وەربگرە و هەر یەکێکیان چاپ بکە. لێرەدا بەکارهێنەرانی تاقیگەی 7 لە پیشان دەدرێن:

users.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
require "connection.php";   // creates $connect (database php_mysql)
$result = mysqli_query($connect, "SELECT id, name, email FROM users ORDER BY id");
?>
<table border="1">
    <tr>
        <th>ID</th>
        <th>Name</th>
        <th>Email</th>
    </tr>
<?php
while ($row = mysqli_fetch_assoc($result)) {
    echo "    <tr>\n";
    echo "        <td>" . htmlspecialchars($row["id"]) . "</td>\n";
    echo "        <td>" . htmlspecialchars($row["name"]) . "</td>\n";
    echo "        <td>" . htmlspecialchars($row["email"]) . "</td>\n";
    echo "    </tr>\n";
}
?>
</table>
<p><?php echo mysqli_num_rows($result); ?> users</p>
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 3

No visitor input in this query, so mysqli_query() is fine. Choose the columns you need — never show the password column.

هیچ نێوخستەیەکی سەردانکەر لەم پرسەدا نییە، بۆیە mysqli_query() باشە. ئەو ستوونانە هەڵبژێرە کە پێویستتن — هەرگیز پیشان مەدە.

Lineدێڕی 12

Each call returns the next row as an associative array; at the end it returns null and the loop stops.

هەر بانگکردنێک دواتر وەک دەگەڕێنێتەوە؛ لە کۆتاییدا null دەگەڕێنێتەوە و ڕادەوەستێت.

Lineدێڕی 13-17

One table row <tr> per database row, opened and closed inside the loop. Every value goes through htmlspecialchars().

یەک <tr> بۆ هەر ، لە ناو دەکرێتەوە و دادەخرێت. هەموو نرخێک بە htmlspecialchars()دا تێدەپەڕێت.

Lineدێڕی 21

mysqli_num_rows() counts the rows of the result.

mysqli_num_rows() ئەنجامەکە دەژمێرێت.

PHP Full Course for free 🐘Bro Code · Englishئینگلیزی · 3M viewsبینین · 65K likesلایک · Partبەشی 3:45:00–3:49:37 (ofلە 4:00:00)

How to create a function, send values in with arguments, and get a result back with return, with a short practice example.

چۆن دروست بکەیت، بە بنێریتە ناوی و بە return ئەنجامێک لێی وەربگریتەوە، لەگەڵ ڕاهێنانێکی کورت.

Watch 3:45:00–3:49:37: SELECT, mysqli_num_rows and a while loop

لە ⁦3:45:00⁩ تا ⁦3:49:37⁩ سەیر بکە: SELECT، mysqli_num_rows() و .

Reading rowsخوێندنەوەی تۆمارەکان

What "fetch" means, and the three result modesواتای «fetch» و سێ شێوازی ئەنجام

To means to go and bring back data from a source — a database, a file or a web service. With MySQLi it means taking a row (or rows) out of a query's . mysqli_fetch_array() fetches one row and returns it as an array. It has two arguments: the result set, and an optional constant that chooses the array type. Each call fetches the next row; when there are no more rows it returns null (and false on a failure).

واتە چوون و هێنانەوەی زانیاری لە سەرچاوەیەکەوە — ، فایل یان . لە MySQLiدا واتە دەرهێنانی (یان چەند ) لە . mysqli_fetch_array() یەک دەهێنێت و وەک دەیگەڕێنێتەوە. دوو هەیە: و ئارەزوومەندانە کە جۆری هەڵدەبژێرێت. هەر بانگکردنێک دواتر دەهێنێت؛ کاتێک نەما null دەگەڕێنێتەوە (و لە کاتی شکستدا false).

Diagram: one row of the result set (sId 1, sName Aram, Marks 85) is fetched in three ways. BOTH, the default of mysqli_fetch_array($result) with MYSQLI_BOTH, gives six elements: [0] and [sId] => 1, [1] and [sName] => Aram, [2] and [Marks] => 85. ASSOC, mysqli_fetch_assoc($result) or fetch_array with MYSQLI_ASSOC, gives three elements with name keys. NUM, mysqli_fetch_row($result) or fetch_array with MYSQLI_NUM, gives three elements with number keys 0, 1, 2. Each call takes the next row; null when no rows are left.وێنە: یەک تۆمار لە کۆمەڵەی ئەنجام (sId 1، sName Aram، Marks 85) بە سێ شێوە دەهێنرێت. BOTH، بنەڕەتیی mysqli_fetch_array($result) لەگەڵ MYSQLI_BOTH، شەش دانە دەدات: [0] و [sId] => 1، [1] و [sName] => Aram، [2] و [Marks] => 85. ASSOC، mysqli_fetch_assoc($result) یان fetch_array لەگەڵ MYSQLI_ASSOC، سێ دانە بە کلیلی ناو دەدات. NUM، mysqli_fetch_row($result) یان fetch_array لەگەڵ MYSQLI_NUM، سێ دانە بە کلیلی ژمارەیی 0، 1، 2 دەدات. هەر بانگکردنێک تۆماری دواتر دەهێنێت؛ کاتێک تۆمار نەما null.
The three result modes of mysqli_fetch_array(): BOTH (default), ASSOC and NUM.سێ شێوازی ئەنجامی ⁦mysqli_fetch_array(): BOTH⁩ (بنەڕەتی)، ASSOC و NUM.CêIT
The modes of mysqli_fetch_array()شێوازەکانی ⁦mysqli_fetch_array()⁩
Mode constant شێوازKeys in the arrayکلیلەکانی ناو Same asهەمان وەک
MYSQLI_BOTHnumbers and column names (the default)ژمارە و ناوی (بنەڕەتی)mysqli_fetch_array($result)
MYSQLI_ASSOCcolumn names onlyتەنها ناوی mysqli_fetch_assoc($result)
MYSQLI_NUMnumbers only (0, 1, 2, ...)تەنها ژمارە (⁦0⁩، 1، 2، ...)mysqli_fetch_row($result)
Quick checkپشکنینی خێراmysqli_fetch_array($result) without a second argument returns…mysqli_fetch_array($result) بەبێ دووەم دەگەڕێنێتەوە…

The modes in codeشێوازەکان لە کۆددا

PHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
require "connection.php";   // the school database of the previous lecture: it creates $con
$result = mysqli_query($con, "SELECT sId, sName, Marks FROM result");

$row = mysqli_fetch_array($result);                // MYSQLI_BOTH (default): numbers AND names
echo $row[1] . " = " . $row["sName"] . "<br>";     // both keys work

$row = mysqli_fetch_array($result, MYSQLI_ASSOC);  // names only, like mysqli_fetch_assoc()
echo $row["sName"] . "<br>";

$row = mysqli_fetch_array($result, MYSQLI_NUM);    // numbers only, like mysqli_fetch_row()
echo $row[1] . "<br>";
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 5-6

Default mode: $row[1] and $row["sName"] both give "Aram".

شێوازی بنەڕەتی: $row[1] و $row["sName"] هەردووکیان «Aram» دەدەن.

Lineدێڕی 8

Names only, for the second row.

تەنها ناو، بۆ دووەم.

Lineدێڕی 11

Numbers only, for the third row.

تەنها ژمارە، بۆ سێیەم.

PHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
// The three result modes, shown with PDO + SQLite (PDO uses FETCH_BOTH / FETCH_ASSOC / FETCH_NUM)
$db = new PDO("sqlite::memory:");
$db->exec("CREATE TABLE result (sId INT, sName TEXT, Marks INT)");
$db->exec("INSERT INTO result VALUES (1, 'Aram', 85), (2, 'Shilan', 92), (3, 'Karwan', 78)");
$stmt = $db->query("SELECT sId, sName, Marks FROM result");

echo "BOTH:  "; print_r($stmt->fetch(PDO::FETCH_BOTH));
echo "ASSOC: "; print_r($stmt->fetch(PDO::FETCH_ASSOC));
echo "NUM:   "; print_r($stmt->fetch(PDO::FETCH_NUM));
echo "No rows left: "; var_dump($stmt->fetch());

The notes' example uses the default mode in a loop. Corrected so that the values are escaped:

نموونەی تێبینییەکان شێوازی بنەڕەتی لە بەکار دەهێنێت. ڕاستکراوەتەوە بۆ ئەوەی نرخەکان پاک بکرێنەوە:

PHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
$conn = mysqli_connect("localhost", "username", "password", "database");

$result = mysqli_query($conn, "SELECT id, name FROM mytable");

// Each call fetches the next row; at the end it returns null and the loop stops
while ($row = mysqli_fetch_array($result)) {
    echo htmlspecialchars($row["id"]) . " - " . htmlspecialchars($row["name"]) . "<br>";
}

mysqli_close($conn);
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 2

Example login details: put your own host, user, password and database here.

زانیاریی نموونەیی بۆ پەیوەندی: هۆست، بەکارهێنەر، و خۆت لێرە دابنێ.

Lineدێڕی 7

The loop runs once per row; mysqli_fetch_array() returns null after the last row.

بۆ هەر یەک جار دەخولێتەوە؛ mysqli_fetch_array() دوای دوا null دەگەڕێنێتەوە.

Reading rowsخوێندنەوەی تۆمارەکان

Counting rows: mysqli_num_rows()ژماردنی تۆمارەکان: ⁦mysqli_num_rows()⁩

mysqli_num_rows($result) tells you how many rows a SELECT returned. In object style the same number is the property $result->num_rows (no brackets).

mysqli_num_rows($result) پێت دەڵێت SELECTێک چەند گەڕاندووەتەوە. لە شێوازی هەمان ژمارە $result->num_rowsـە (بێ کەوانە).

PHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
$conn = mysqli_connect("localhost", "username", "password", "database_name");

$result = mysqli_query($conn, "SELECT * FROM users");

$num_rows = mysqli_num_rows($result);   // procedural style
// $num_rows = $result->num_rows;       // object style: the same number

echo "There are " . $num_rows . " rows in the users table.";
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 6

The function: it takes the result.

: ئەنجامەکە وەردەگرێت.

Lineدێڕی 7

The property: the same number.

: هەمان ژمارە.

Use mysqli_num_rows() when you fetch the rows anyway (for example to print "No record found"). If you only need the number, let the database count with COUNT(*): only one small number travels back instead of every row.

mysqli_num_rows() بەکار بهێنە کاتێک بەهەرحاڵ دەهێنیت (بۆ نموونە بۆ چاپکردنی «⁦No record found⁩»). ئەگەر تەنها ژمارەکەت پێویستە، بهێڵە بە COUNT(*) بیژمێرێت: تەنها یەک ژمارەی بچووک دەگەڕێتەوە لە جیاتی هەموو .

PHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
$db = new PDO("sqlite::memory:");
$db->exec("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, city TEXT)");
$db->exec("INSERT INTO users (name, city) VALUES ('Dilan','Erbil'), ('Rezan','Duhok'), ('Hana','Erbil'), ('Soran','Erbil')");

// 1. Fetch the rows, then count them in PHP (like mysqli_num_rows)
$rows = $db->query("SELECT * FROM users WHERE city = 'Erbil'")->fetchAll();
echo "Rows fetched: " . count($rows) . "\n";

// 2. Let the database count: only one small number travels back
$total = $db->query("SELECT COUNT(*) FROM users")->fetchColumn();
echo "COUNT(*): " . $total . "\n";
Quick checkپشکنینی خێراYou only need to show how many users live in Erbil. What is the best query?تەنها دەتەوێت پیشان بدەیت چەند بەکارهێنەر لە Erbil دەژین. باشترین کامەیە؟

One page: check, insert with an image, and listیەک لاپەڕە: پشکنین، زیادکردن لەگەڵ وێنە و لیستکردن

The practical notes contain a bigger page that does everything at once. The table users has id, username, password and filename (the name of an uploaded image). When the form is submitted the page:

تێبینییە پراکتیکییەکان لاپەڕەیەکی گەورەتریان تێدایە کە هەموو شتێک پێکەوە دەکات. users id، username، password و filename (ناوی وێنەیەکی بارکراو)ی هەیە. کاتێک دەنێردرێت، لاپەڕەکە:

  1. checks whether the username already exists (a SELECT with WHERE username = ...);
  2. if it is new, checks the uploaded image (not empty, not already on the server, only png or jpg), saves the user and moves the file into uploads/;
  3. lists all users in a table and writes already exists in red next to the name that was tried again;
  4. shows an insert form in the last table row.
  1. دەپشکنێت ئایا پێشتر هەیە (SELECTێک بە WHERE username = ...)؛
  2. ئەگەر نوێ بێت، وێنە بارکراوەکە دەپشکنێت (بەتاڵ نەبێت، پێشتر لەسەر نەبێت، تەنها png یان jpg)، بەکارهێنەرەکە هەڵدەگرێت و فایلەکە دەگوازێتەوە بۆ uploads/؛
  3. هەموو بەکارهێنەران لە لیست دەکات و لە تەنیشت ئەو ناوەی دووبارە تاقی کرایەوە ⁦already exists⁩ بە سوور دەنووسێت؛
  4. لە دوا زیادکردن پیشان دەدات.

Here is that page rewritten safely. The SELECT that checks the username uses visitor input, so it is a prepared statement too; its result is read with mysqli_stmt_get_result().

ئەمە ئەو لاپەڕەیەیە کە بە سەلامەتی دووبارە نووسراوەتەوە. ئەو SELECTـەی دەپشکنێت نێوخستەی سەردانکەر بەکار دەهێنێت، بۆیە ئەویش ؛ ئەنجامەکەی بە mysqli_stmt_get_result() دەخوێندرێتەوە.

users_page.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
$con = mysqli_connect("localhost", "root", "", "MyStudy");   // throws if it fails (PHP 8.1+)
mysqli_set_charset($con, "utf8mb4");

$alreadyExists = false;
$username = "";
$message = "";

if (isset($_POST["SubBtn"])) {
    $username = trim($_POST["userInput"]);

    if ($username === "") {
        $message = "Username cannot be empty";
    } else {
        // 1. Does this username already exist? (prepared SELECT)
        $stmt = mysqli_prepare($con, "SELECT id FROM users WHERE username = ?");
        mysqli_stmt_bind_param($stmt, "s", $username);
        mysqli_stmt_execute($stmt);
        $result = mysqli_stmt_get_result($stmt);
        $alreadyExists = mysqli_num_rows($result) > 0;
    }

    if ($username !== "" && !$alreadyExists) {
        // 2. New user: check the uploaded image
        $file_name = basename($_FILES["FileName"]["name"]);   // no folder names from the visitor
        $file_tmp = $_FILES["FileName"]["tmp_name"];
        $target_file = "uploads/" . $file_name;
        $parts = explode(".", $file_name);
        $extension = strtolower(end($parts));
        $allowed = ["png", "jpg"];

        if ($file_name === "") {
            $message = "Image input cannot be empty";
        } elseif (file_exists($target_file)) {
            $message = "Image file already exists.";
        } elseif (!in_array($extension, $allowed)) {
            $message = "Only PNG and JPG images are allowed.";
        } else {
            // 3. Save the user (prepared INSERT, hashed password), then keep the file
            $hash = password_hash($_POST["passInput"], PASSWORD_DEFAULT);
            $stmt = mysqli_prepare($con, "INSERT INTO users (username, password, filename) VALUES (?, ?, ?)");
            mysqli_stmt_bind_param($stmt, "sss", $username, $hash, $file_name);
            mysqli_stmt_execute($stmt);
            move_uploaded_file($file_tmp, $target_file);
            $message = "Success: the user was added.";
        }
    }
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Users</title>
</head>
<body>
<p><?php echo htmlspecialchars($message); ?></p>

<form method="post" enctype="multipart/form-data">
<table border="1">
    <tr>
        <th>ID</th>
        <th>Username</th>
        <th>Image</th>
    </tr>
<?php
$selectQuery = mysqli_query($con, "SELECT id, username, filename FROM users ORDER BY id");
while ($row = mysqli_fetch_assoc($selectQuery)) {
?>
    <tr>
        <td><?php echo $row["id"]; ?></td>
        <td>
            <?php echo htmlspecialchars($row["username"]);
            if ($alreadyExists && $row["username"] === $username) {
                echo ' <span style="color:red">already exists</span>';
            } ?>
        </td>
        <td><img width="50" height="60" alt="" src="uploads/<?php echo htmlspecialchars($row["filename"]); ?>"></td>
    </tr>
<?php
}
?>
    <tr>
        <td></td>
        <td>
            <input type="text" name="userInput" placeholder="username">
            <input type="password" name="passInput" placeholder="password">
        </td>
        <td>
            <input type="file" name="FileName">
            <input type="submit" name="SubBtn" value="Insert">
        </td>
    </tr>
</table>
</form>
</body>
</html>
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 2-3

Connect (the notes used database MyStudy).

پەیوەندی بکە (تێبینییەکان MyStudyیان بەکار هێنا).

Lineدێڕی 16-20

A prepared SELECT; mysqli_stmt_get_result() gives a normal result, and mysqli_num_rows() > 0 means the name is taken.

SELECTـێکی ئامادەکراو؛ mysqli_stmt_get_result() ئەنجامێکی ئاسایی دەدات و mysqli_num_rows() > 0 واتە ناوەکە گیراوە.

Lineدێڕی 25

basename() removes any folder part from the uploaded file name.

basename() هەر بەشێکی فۆڵدەر لە ناوی فایلە بارکراوەکە لادەبات.

Lineدێڕی 32-37

The same three image checks as the notes: empty, already exists, allowed extension. More on uploads: File uploads.

هەمان سێ پشکنینی وێنەی تێبینییەکان: بەتاڵ، پێشتر هەیە، پاشگری ڕێگەپێدراو. زیاتر دەربارەی بارکردن: بارکردنی فایل.

Lineدێڕی 40-43

Hash the password; a prepared INSERT with three strings.

هاش بکە؛ INSERTـێکی ئامادەکراو بە سێ .

Lineدێڕی 59-60

The <form> wraps the whole table (a form directly inside <tr> is invalid HTML).

<form> هەموو دەگرێتەوە ( ڕاستەوخۆ لە ناو <tr>دا HTMLـی نادروستە).

Lineدێڕی 68-81

One complete <tr>...</tr> per user. The password column is not shown.

یەک <tr>...</tr>ـی تەواو بۆ هەر بەکارهێنەرێک. پیشان نادرێت.

Lineدێڕی 74-76

Mark the name that was tried again in red.

ئەو ناوەی دووبارە تاقی کرایەوە بە سوور نیشان بکە.

One page: check, insert with an image, and listیەک لاپەڕە: پشکنین، زیادکردن لەگەڵ وێنە و لیستکردن

What was wrong in the notes' versionچی هەڵە بوو لە وەشانەکەی تێبینییەکاندا

Long lists: LIMIT, OFFSET and paginationلیستە درێژەکان: LIMIT، OFFSET و لاپەڕەبەندی

MySQL provides a that sets the maximum number of records a SELECT returns. Returning a large number of records can slow a page down, so LIMIT is very useful on large tables. Together with OFFSET, which skips a number of rows first, it makes it easy to show multi-page results: .

MySQL دابین دەکات کە زۆرترین ژمارەی ئەو تۆمارانە دیاری دەکات کە SELECTێک دەیانگەڕێنێتەوە. گەڕاندنەوەی ژمارەیەکی زۆر دەتوانێت لاپەڕەیەک هێواش بکات، بۆیە LIMIT لەسەر گەورەکان زۆر بەسوودە. لەگەڵ OFFSET، کە سەرەتا ژمارەیەک تێدەپەڕێنێت، پیشاندانی ئەنجام لە چەند لاپەڕەیەکدا ئاسان دەکات: .

Diagram: twelve rows with ids 1 to 12 in order. For SELECT * FROM orders ORDER BY id LIMIT 4 OFFSET 4, rows 1–4 are skipped (OFFSET 4), rows 5–8 are returned (LIMIT 4), rows 9–12 are not returned. Below, pagination with 4 rows per page: page 1 is LIMIT 4 OFFSET 0 (rows 1–4), page 2 is LIMIT 4 OFFSET 4 (rows 5–8), page 3 is LIMIT 4 OFFSET 8 (rows 9–12). Formula: OFFSET = (page − 1) × LIMIT. Always add ORDER BY.وێنە: دوازدە تۆمار بە id ـی 1 تا 12 بە ڕیز. بۆ SELECT * FROM orders ORDER BY id LIMIT 4 OFFSET 4، تۆمارەکانی 1–4 تێدەپەڕێنرێن (OFFSET 4)، تۆمارەکانی 5–8 دەگەڕێنرێنەوە (LIMIT 4)، تۆمارەکانی 9–12 ناگەڕێنرێنەوە. لە خوارەوە، لاپەڕەبەندی بە 4 تۆمار بۆ هەر لاپەڕەیەک: لاپەڕەی 1 LIMIT 4 OFFSET 0ـە (تۆمارەکانی 1–4)، لاپەڕەی 2 LIMIT 4 OFFSET 4ـە (5–8)، لاپەڕەی 3 LIMIT 4 OFFSET 8ـە (9–12). هاوکێشە: OFFSET = (ژمارەی لاپەڕە − 1) × LIMIT. هەمیشە ORDER BY زیاد بکە.
LIMIT takes rows, OFFSET skips rows; together they cut a long result into pages.LIMIT وەردەگرێت، OFFSET تێدەپەڕێنێت؛ پێکەوە ئەنجامێکی درێژ دەکەن بە چەند لاپەڕەیەک.CêIT

Long lists: LIMIT, OFFSET and paginationلیستە درێژەکان: LIMIT، OFFSET و لاپەڕەبەندی

LIMIT and OFFSET in SQLLIMIT و OFFSET لە SQLدا

The course examples, run on a table Orders with ids 1 to 50 (an ORDER BY id is added so that "first" always means the same rows):

نموونەکانی وانەکە، لەسەر Orders بە id ـی 1 تا ⁦50⁩ جێبەجێ کراون (ORDER BY id زیاد کراوە بۆ ئەوەی «یەکەم» هەمیشە هەمان بێت):

SQLRuns in the databaseلە ناو بنکەدراوە کار دەکات
SELECT * FROM Orders ORDER BY id LIMIT 30;
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 1

At most 30 rows: the first 30.

زۆرترین ⁦30⁩ : ⁦30⁩ی یەکەم.

SQLRuns in the databaseلە ناو بنکەدراوە کار دەکات
SELECT * FROM Orders ORDER BY id LIMIT 10 OFFSET 15;
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 1

Skip 15 rows, then return 10: rows 16 to 25.

⁦15⁩ تێبپەڕێنە، پاشان ⁦10⁩ بگەڕێنەوە: ⁦16⁩ تا ⁦25⁩.

SQLRuns in the databaseلە ناو بنکەدراوە کار دەکات
SELECT * FROM Orders ORDER BY id LIMIT 15, 10;
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 1

MySQL's short form: LIMIT offset, count — careful, here the offset comes first. Same result as the previous query.

شێوە کورتەکەی MySQL: LIMIT offset, count — ئاگاداربە، لێرەدا offset یەکەمە. هەمان ئەنجامی پێشوو.

The student notes give three more examples on a table my_table:

تێبینییەکانی خوێندکار سێ نموونەی تر لەسەر my_table دەدەن:

LIMIT and OFFSET examples (results checked on 50 ordered rows)نموونەکانی LIMIT و OFFSET (ئەنجامەکان لەسەر ⁦50⁩ ڕیزکراو پشکنراون)
QueryResultئەنجام
SELECT * FROM my_table LIMIT 10;the first 10 rows (rows 1–10)⁦10⁩ یەکەم ( ⁦1–10⁩)
SELECT * FROM my_table LIMIT 10 OFFSET 5;10 rows starting from the 6th row: the first 5 are skipped (rows 6–15)⁦10⁩ لە شەشەمەوە: 5ی یەکەم تێدەپەڕێنرێن ( ⁦6–15⁩)
SELECT * FROM my_table LIMIT 10 OFFSET 10;skip the first 10, return the next 10 (rows 11–20)⁦10⁩ی یەکەم تێبپەڕێنە، ⁦10⁩ی دواتر بگەڕێنەوە ( ⁦11–20⁩)

The summary shows a picture of a Customers table and the query SELECT first_name, last_name FROM Customers LIMIT 2 OFFSET 3;. With five customers it skips three and returns the 4th and 5th:

پوختەکە وێنەی Customers و SELECT first_name, last_name FROM Customers LIMIT 2 OFFSET 3; پیشان دەدات. بە پێنج کڕیار، سێیان تێدەپەڕێنێت و چوارەم و پێنجەم دەگەڕێنێتەوە:

Customers (names replaced with neutral ones); LIMIT 2 OFFSET 3 returns the rows in boldCustomers (ناوەکان بە ناوی بێلایەن گۆڕاون)؛ ⁦LIMIT 2 OFFSET 3⁩ تۆخەکان دەگەڕێنێتەوە
customer_idfirst_namelast_nameagecountry
1AramAli31USA
2ShilanOmar22USA
3KarwanAziz22UK
4DilanHasan25UK
5RezanSalih28UAE
SQLRuns in the databaseلە ناو بنکەدراوە کار دەکات
SELECT first_name, last_name FROM Customers ORDER BY customer_id LIMIT 2 OFFSET 3;
Quick checkپشکنینی خێراWhat does LIMIT 10 OFFSET 15 return?LIMIT 10 OFFSET 15 چی دەگەڕێنێتەوە؟

Pagination in MySQL - offset vs. cursorPlanetScale · Englishئینگلیزی · 83K viewsبینین · 4.8K likesلایک · Partبەشی 1:25–6:12 (ofلە 13:19)

Shows with clear animations how LIMIT and OFFSET split a long list into pages, and why ORDER BY is needed.

بە ڕوون پیشان دەدات چۆن و لیستێکی درێژ دابەشی چەند لاپەڕەیەک دەکەن و بۆچی پێویستە.

Watch 1:25–6:12: ORDER BY, then LIMIT and OFFSET pagination

لە ⁦1:25⁩ تا ⁦6:12⁩ سەیر بکە: ، پاشان بە LIMIT و OFFSET.

Long lists: LIMIT, OFFSET and paginationلیستە درێژەکان: LIMIT، OFFSET و لاپەڕەبەندی

Pagination in PHPلاپەڕەبەندی لە PHPدا

For page n with k rows per page: OFFSET = (n − 1) × k. Page 1 skips 0 rows, page 2 skips k, page 3 skips 2k. The page number arrives in the URL (?page=3), so it is visitor input: turn it into an integer and send it with a prepared statement.

بۆ لاپەڕەی n بە k بۆ هەر لاپەڕەیەک: OFFSET = (n − 1) × k. لاپەڕەی 1 هیچ تێناپەڕێنێت، لاپەڕەی 2 k تێدەپەڕێنێت، لاپەڕەی 3 دوو هێندەی k. ژمارەی لاپەڕە لە URLـدا دێت (?page=3)، بۆیە : بیکە بە و بە بینێرە.

orders.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
require "dbcon.php";

$perPage = 10;                                            // rows on one page
$page = isset($_GET["page"]) ? (int) $_GET["page"] : 1;   // ?page=3 in the URL
if ($page < 1) {
    $page = 1;
}
$offset = ($page - 1) * $perPage;                         // page 3 -> skip 20 rows

$stmt = mysqli_prepare($con, "SELECT id, product FROM Orders ORDER BY id LIMIT ? OFFSET ?");
mysqli_stmt_bind_param($stmt, "ii", $perPage, $offset);   // both are integers
mysqli_stmt_execute($stmt);
$result = mysqli_stmt_get_result($stmt);

while ($row = mysqli_fetch_assoc($result)) {
    echo $row["id"] . ": " . htmlspecialchars($row["product"]) . "<br>\n";
}
echo '<a href="?page=' . ($page + 1) . '">Next page</a>';
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 5-8

(int) turns any text into a whole number; pages below 1 become 1.

(int) هەر دەقێک دەکات بە ؛ لاپەڕەی کەمتر لە 1 دەبێت بە 1.

Lineدێڕی 9

The formula: page 3 → (3 − 1) × 10 = 20 rows skipped.

هاوکێشەکە: لاپەڕەی 3 ← ⁦(3 − 1) × 10 = 20⁩ تێدەپەڕێنرێن.

Lineدێڕی 11-14

LIMIT and OFFSET values are sent as integers ("ii") through a prepared statement.

نرخەکانی LIMIT و OFFSET وەک ("ii") لە ڕێگەی دەنێردرێن.

Lineدێڕی 19

A link to the next page.

بەستەرێک بۆ لاپەڕەی دواتر.

PHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
// The same page with PDO (run here with SQLite and ?page=3)
$_GET["page"] = "3";
$pdo = new PDO("sqlite::memory:");
$pdo->exec("CREATE TABLE Orders (id INTEGER PRIMARY KEY, product TEXT)");
for ($i = 1; $i <= 50; $i++) { $pdo->exec("INSERT INTO Orders VALUES ($i, 'Item $i')"); }

$perPage = 10;
$page = isset($_GET["page"]) ? (int) $_GET["page"] : 1;
if ($page < 1) {
    $page = 1;
}
$offset = ($page - 1) * $perPage;

$stmt = $pdo->prepare("SELECT id, product FROM Orders ORDER BY id LIMIT :lim OFFSET :off");
$stmt->bindValue(":lim", $perPage, PDO::PARAM_INT);   // send as numbers, not text
$stmt->bindValue(":off", $offset, PDO::PARAM_INT);
$stmt->execute();

foreach ($stmt as $row) {
    echo $row["id"] . ": " . htmlspecialchars($row["product"]) . "<br>\n";
}
echo '<a href="?page=' . ($page + 1) . '">Next page</a>';
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 16-17

With PDO, bind LIMIT/OFFSET with PDO::PARAM_INT. If they are sent as text, MySQL rejects LIMIT '10'.

لە PDOدا ⁦LIMIT/OFFSET⁩ بە PDO::PARAM_INT ببەستەوە. ئەگەر وەک دەق بنێردرێن، MySQL LIMIT '10' ڕەت دەکاتەوە.

Common mistakesهەڵە باوەکان

Mistakes when saving and reading dataهەڵەکان لە کاتی هەڵگرتن و خوێندنەوەی زانیاریدا
MistakeهەڵەWhat happensچی ڕوو دەداتFixچارەسەر
VALUES ('$name', '$email')SQL injection; O'Brien breaks the query؛ ⁦O'Brien⁩ تێک دەداتPrepared statement with ? بە ?
VALUES ('?', '?')Quotes turn the markers into plain textکۆتەیشنەکان نیشانەکان دەکەن بە دەقی ئاساییWrite ? without quotes? بەبێ کۆتەیشن بنووسە
mysqli_stmt_bind_param($stmt, "s", $a, $b)ArgumentCountError: the type string needs one letter per valueArgumentCountError: زنجیرەی جۆر بۆ هەر نرخێک یەک پیتی پێویستە"ss"
sha1($password) / md5($password)Weak hashes that can be looked upهاشی لاواز کە دەکرێت بدۆزرێنەوەpassword_hash() + password_verify()
mysql_error()Call to undefined function mysql_error()try/catch, or mysqli_error($con)try/catch، یان mysqli_error($con)
Column passwrod in the table passwrod لە Unknown column 'password' in 'field list'Rename the columnناوی بگۆڕە
mysqli_num_rows($result <= 0)TypeError (wrong brackets)TypeError (کەوانەی هەڵە)mysqli_num_rows($result) <= 0
echo $row["name"];XSS if the name contains HTMLXSS ئەگەر ناوەکە HTMLـی تێدا بێتecho htmlspecialchars($row["name"]);
LIMIT 10 OFFSET 15 (no ORDER BY)Pages may change or repeat rowsلاپەڕەکان لەوانەیە بگۆڕێن یان دووبارە بکەنەوەORDER BY id LIMIT 10 OFFSET 15
LIMIT 15, 10Read as "15 rows from row 10" — wrongوا دەخوێندرێتەوە «⁦15⁩ لە ⁦10⁩ـەوە» — هەڵەیەOffset first: 10 rows after skipping 15سەرەتا ⁦offset: 10⁩ دوای تێپەڕاندنی ⁦15⁩

Exercisesڕاهێنانەکان

Exerciseڕاهێنان1. Lab 7: registration with PHP and MySQL1. تاقیگەی 7: خۆتۆمارکردن بە PHP و MySQL

Do Lab 7 completely: with the php_mysql database and users table, build index.php (registration form, validation, insert) and connection.php, so that a visitor can create an account. Test it with: an empty username, two different passwords, a correct registration, and the same email a second time. Check the new row in phpMyAdmin's Browse tab.

تاقیگەی 7 بە تەواوی بکە: بە php_mysql و users، index.php ( خۆتۆمارکردن، پشکنین، زیادکردن) و connection.php دروست بکە، بۆ ئەوەی سەردانکەرێک بتوانێت هەژمار دروست بکات. بەمانە تاقی بکەرەوە: بەتاڵ، دوو جیاواز، خۆتۆمارکردنێکی دروست و هەمان بۆ جاری دووەم. نوێیەکە لە تابی Browseـی phpMyAdmin بپشکنە.

Hintئاماژە

Use the corrected files in Lab 7. In Browse, the password column must start with $2y$.

فایلە ڕاستکراوەکانی تاقیگەی 7 بەکار بهێنە. لە Browseدا، دەبێت بە $2y$ دەست پێ بکات.

Show answerوەڵامەکە پیشان بدە

The complete solution is connection.php plus index.php. The four tests should print, in order: Username is required; The passwords did not match, please try again; Account created successfully, you can log in now; This email address already has an account. In phpMyAdmin the new row shows the name, the email and a 60-character hash that starts with $2y$, never the password itself.

چارەسەرە تەواوەکە ⁦connection.php⁩ لەگەڵ ⁦index.php⁩ـە. چوار تاقیکردنەوەکە دەبێت بە ڕیز ئەمانە چاپ بکەن: ⁦Username is required⁩؛ ⁦The passwords did not match, please try again⁩؛ ⁦Account created successfully, you can log in now⁩؛ ⁦This email address already has an account⁩. لە phpMyAdminدا نوێیەکە ناو، و هاشێکی ⁦60⁩ پیتی پیشان دەدات کە بە $2y$ دەست پێ دەکات، هەرگیز خودی نا.

Exerciseڕاهێنان2. Show the rows in an HTML table2. تۆمارەکان لە خشتەیەکدا پیشان بدە

Write a page staff.php that shows the rows of the stage_4 table (id, name, age, salary) in an HTML table, sorted by salary from high to low, and prints "No record found" when the table is empty.

لاپەڕەیەکی staff.php بنووسە کە stage_4 (id، name، age، salary) لە پیشان بدات، بەپێی مووچە لە بەرزەوە بۆ نزم ڕیزکراو و کاتێک بەتاڵە «⁦No record found⁩» چاپ بکات.

Show answerوەڵامەکە پیشان بدە
staff.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
require "dbcon.php";
$result = mysqli_query($con, "SELECT id, name, age, salary FROM stage_4 ORDER BY salary DESC");

if (mysqli_num_rows($result) === 0) {
    echo "<p>No record found</p>";
} else {
    echo "<table border=\"1\">\n<tr><th>ID</th><th>Name</th><th>Age</th><th>Salary</th></tr>\n";
    while ($row = mysqli_fetch_assoc($result)) {
        echo "<tr><td>" . $row["id"] . "</td><td>" . htmlspecialchars($row["name"]) . "</td><td>"
            . $row["age"] . "</td><td>" . $row["salary"] . "</td></tr>\n";
    }
    echo "</table>";
}
Exerciseڕاهێنان3. A safe search3. گەڕانێکی سەلامەت

Add a search box to staff.php: the visitor types a name (?q=... in the URL) and the page shows only the rows whose name starts with that text. Use a prepared statement.

خانەیەکی گەڕان زیاد بکە بۆ staff.php: سەردانکەر ناوێک دەنووسێت (?q=... لە URLـدا) و لاپەڕەکە تەنها ئەو پیشان دەدات کە ناوەکەیان بەو دەقە دەست پێ دەکات. بەکار بهێنە.

Hintئاماژە

WHERE name LIKE ? and bind the value $q . "%".

WHERE name LIKE ? و نرخی $q . "%" ببەستەوە.

Show answerوەڵامەکە پیشان بدە
PHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
require "dbcon.php";
$q = isset($_GET["q"]) ? trim($_GET["q"]) : "";
$pattern = $q . "%";                       // starts with ...

$stmt = mysqli_prepare($con, "SELECT name, salary FROM stage_4 WHERE name LIKE ? ORDER BY name");
mysqli_stmt_bind_param($stmt, "s", $pattern);
mysqli_stmt_execute($stmt);
$result = mysqli_stmt_get_result($stmt);

while ($row = mysqli_fetch_assoc($result)) {
    echo htmlspecialchars($row["name"]) . ": " . $row["salary"] . "<br>";
}

The % is added in PHP, not typed into the SQL, so the visitor's text is still only a value.

% لە PHPـدا زیاد دەکرێت، نەک لە ناو SQLـدا بنووسرێت، بۆیە دەقی سەردانکەر هێشتا تەنها نرخێکە.

Exerciseڕاهێنان4. Pagination by hand4. لاپەڕەبەندی بە دەست

A shop shows 8 products per page. Which LIMIT and OFFSET does page 5 need? Which rows does it show (with ORDER BY id, ids 1, 2, 3, ...)?

فرۆشگایەک 8 کاڵا لە هەر لاپەڕەیەکدا پیشان دەدات. لاپەڕەی 5 پێویستی بە کام LIMIT و OFFSET هەیە؟ کام پیشان دەدات (بە ORDER BY id، id ـی 1، 2، 3، ...)؟

Show answerوەڵامەکە پیشان بدە

LIMIT 8 OFFSET 32, because OFFSET = (5 − 1) × 8 = 32. It shows the rows with ids 33 to 40. In MySQL's short form: LIMIT 32, 8.

LIMIT 8 OFFSET 32، چونکە ⁦OFFSET = (5 − 1) × 8 = 32⁩. id ـی ⁦33⁩ تا ⁦40⁩ پیشان دەدات. بە شێوە کورتەکەی MySQL: LIMIT 32, 8.

Exerciseڕاهێنان5. Find the bugs: a members page5. هەڵەکان بدۆزەرەوە: لاپەڕەی ئەندامان

A student's first page that reads a table, members (id, first_name, last_name, city), looks like this. It runs and shows the names, but it has at least six problems. Find them, then write a correct version. (One member typed Hana <b> as a first name.)

یەکەم لاپەڕەی خوێندکارێک کە دەخوێنێتەوە، members (id, first_name, last_name, city)، بەم شێوەیەیە. کار دەکات و ناوەکان پیشان دەدات، بەڵام لانیکەم شەش کێشەی هەیە. بیاندۆزەرەوە، پاشان وەشانێکی دروست بنووسە. (ئەندامێک Hana <b>ـی وەک ناوی یەکەم نووسیوە.)

The student's version — do not copyوەشانەکەی خوێندکارەکە — کۆپی مەکەmembers.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php include('includes/config.php'); ?>
<style> td { padding: 50px; border: 1px solid black; } </style>
<?php
$sql = mysqli_query($db, "SELECT * FROM members");
while ($row = mysqli_fetch_assoc($sql)) { ?>
    <table><td><?php echo $row['first_name']; ?></td></table>
<?php } ?>
Hintئاماژە

Look at the HTML the loop builds, at what is printed without protection, at what the query reads, and at what happens when config.php is missing.

سەیری ئەو HTMLـە بکە کە دروستی دەکات، ئەوەی بەبێ پاراستن چاپ دەکرێت، ئەوەی دەیخوێنێتەوە و ئەوەی ڕوودەدات ئەگەر config.php نەبێت.

Show answerوەڵامەکە پیشان بدە
  1. A new <table> for every row. The loop must print only the rows; open <table> once before it and close it once after it.
  2. <td> without <tr>. Every cell belongs inside a table row: <tr><td>…</td></tr>.
  3. No htmlspecialchars(). A name such as Hana <b> becomes real HTML (everything after it turns bold); a script in a name would run: .
  4. SELECT * for one column. Ask only for what the page shows: SELECT first_name, and add ORDER BY so the order is fixed.
  5. include for a file the page cannot work without. If config.php is missing, include only warns and the page goes on to fail with mysqli_query(): Argument #1 ($mysql) must be of type mysqli, null given. require_once stops at once with a clear message (include_once and require_once).
  6. No plan for an empty table, and a misleading name. Print No members yet when there are no rows (mysqli_num_rows()); and $sql holds a result, not SQL, so call it $result.
  1. نوێ بۆ هەر . دەبێت تەنها چاپ بکات؛ <table> یەک جار پێش بکەرەوە و یەک جار دوای ئەو دایبخە.
  2. <td> بەبێ <tr>. هەموو خانەیەک دەبێت لەناو بێت: <tr><td>…</td></tr>.
  3. htmlspecialchars() نییە. ناوێکی وەک Hana <b> دەبێتە HTMLـی ڕاستەقینە (هەموو شتێکی دوای ئەو تۆخ دەبێت)؛ سکریپتێک لەناو ناوێکدا جێبەجێ دەبوو: .
  4. SELECT * بۆ یەک . تەنها داوای ئەوە بکە کە لاپەڕەکە پیشانی دەدات: SELECT first_name و ORDER BY زیاد بکە بۆ ئەوەی ڕیزبەندییەکە جێگیر بێت.
  5. include بۆ فایلێک کە لاپەڕەکە بەبێ ئەو کار ناکات. ئەگەر config.php نەبێت، include تەنها دەدات و لاپەڕەکە بەردەوام دەبێت تا بە ⁦mysqli_query(): Argument #1 ($mysql) must be of type mysqli, null given⁩ دەشکێت. require_once یەکسەر بە پەیامێکی ڕوون دەوەستێت (⁦include_once⁩ و ⁦require_once⁩).
  6. هیچ پلانێک بۆ بەتاڵ نییە و ناوێکی چەواشەکار. کاتێک هیچ نییە ⁦No members yet⁩ چاپ بکە (mysqli_num_rows())؛ $sql هەڵدەگرێت نەک SQL، بۆیە ناوی بنێ $result.
A corrected versionوەشانێکی چاککراوmembers.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
require_once 'includes/config.php';   // stops the page if the file is missing

$result = mysqli_query($db, "SELECT first_name FROM members ORDER BY first_name");
?>
<table>
    <tr><th>First name</th></tr>
<?php if (mysqli_num_rows($result) === 0): ?>
    <tr><td>No members yet</td></tr>
<?php endif; ?>
<?php while ($row = mysqli_fetch_assoc($result)): ?>
    <tr><td><?= htmlspecialchars($row['first_name']) ?></td></tr>
<?php endwhile; ?>
</table>

The style belongs in the page's <head> (or a CSS file), and padding: 50px makes huge cells. The student's comment "table and field names are case-sensitive" is only half true: see Common mistakes.

شێوازەکە ⁦(style)⁩ لە <head>ـی لاپەڕەکەدا (یان فایلێکی CSS) جێی دەبێتەوە و padding: 50px خانەی زۆر گەورە دروست دەکات. خوێندکارەکە «ناوی و » تەنها نیوەی ڕاستە: بڕوانە هەڵە باوەکان.

Summary and reviewپوختە و پێداچوونەوە

  • Fixed values: $con->query($sql) or mysqli_query($con, $sql); check the result with try/catch in PHP 8.1+.
  • Visitor values: prepare (?), bind ("ssi"), execute — or PDO prepare() + execute([...]).
  • Passwords: password_hash() to save (VARCHAR 255), password_verify() to check. Never md5(), sha1() or plain text.
  • Reading: SELECT, then while ($row = mysqli_fetch_assoc($result)); escape output with htmlspecialchars().
  • mysqli_fetch_array() modes: BOTH (default), ASSOC, NUM; mysqli_num_rows() / $result->num_rows counts the result rows.
  • LIMIT n takes at most n rows, OFFSET m skips m rows; page p = LIMIT k OFFSET (p − 1) × k, always with ORDER BY.
  • نرخی جێگیر: $con->query($sql) یان mysqli_query($con, $sql)؛ لە ⁦PHP 8.1+⁩دا ئەنجامەکە بە try/catch بپشکنە.
  • نرخی سەردانکەر: ئامادەکردن (?)، بەستنەوە ("ssi")، جێبەجێکردن — یان PDO prepare() + execute([...]).
  • : password_hash() بۆ هەڵگرتن ⁦(VARCHAR 255)⁩، password_verify() بۆ پشکنین. هەرگیز md5()، sha1() یان دەقی ئاسایی نا.
  • خوێندنەوە: SELECT، پاشان while ($row = mysqli_fetch_assoc($result))؛ بە htmlspecialchars() پاک بکەرەوە.
  • شێوازەکانی mysqli_fetch_array(): BOTH (بنەڕەتی)، ASSOC، NUM؛ mysqli_num_rows() / $result->num_rows ئەنجام دەژمێرێت.
  • LIMIT n زۆرترین n وەردەگرێت، OFFSET m m تێدەپەڕێنێت؛ لاپەڕەی p = LIMIT k OFFSET (p − 1) × k، هەمیشە لەگەڵ ORDER BY.
Review questionsپرسیارەکانی پێداچوونەوە
What is a prepared statement and why use it?ڕستەی ئامادەکراو چییە و بۆچی بەکاری دەهێنین؟

An SQL statement sent with parameter markers (? or :name) instead of values; the values are sent separately, so visitor input can never change the SQL. It prevents SQL injection and handles apostrophes correctly.

فەرمانێکی SQLـە کە لە جیاتی نرخەکان بە (? یان :name) دەنێردرێت؛ نرخەکان بە جیا دەنێردرێن، بۆیە نێوخستەی سەردانکەر هەرگیز ناتوانێت SQLـەکە بگۆڕێت. ڕێگە لە دەگرێت و ئەپۆسترۆفیش بە دروستی هەڵدەگیرێت.

What does "fetch" mean, and what does mysqli_fetch_array() return?«fetch» واتای چییە و mysqli_fetch_array() چی دەگەڕێنێتەوە؟

To fetch is to go and bring back data from a source. mysqli_fetch_array() returns the next row of the result set as an array (by default with number and name keys), or null when no rows are left.

fetch واتە چوون و هێنانەوەی زانیاری لە سەرچاوەیەکەوە. mysqli_fetch_array() دواتری وەک دەگەڕێنێتەوە (بە شێوەی بنەڕەتی بە کلیلی ژمارەیی و ناو)، یان null کاتێک نەما.

What is the difference between mysqli_num_rows($result) and $result->num_rows?جیاوازیی نێوان mysqli_num_rows($result) و $result->num_rows چییە؟

None in the result: the first is the procedural function, the second the property of the result object. Both give the number of rows the SELECT returned.

لە ئەنجامدا هیچ: یەکەمیان پرۆسیجەرییە، دووەمیان ئەنجامەکەیە. هەردووکیان ژمارەی ئەو دەدەن کە SELECT گەڕاندوونیەتەوە.

Explain LIMIT and OFFSET with an example.LIMIT و OFFSET بە نموونەیەک ڕوون بکەرەوە.

LIMIT sets the maximum number of rows returned; OFFSET skips rows first. SELECT * FROM Orders ORDER BY id LIMIT 10 OFFSET 15 skips 15 rows and returns rows 16–25. Together they are used for pagination.

LIMIT زۆرترین ژمارەی گەڕاوە دیاری دەکات؛ OFFSET سەرەتا تێدەپەڕێنێت. SELECT * FROM Orders ORDER BY id LIMIT 10 OFFSET 15 پازدە تێدەپەڕێنێت و ⁦16–25⁩ دەگەڕێنێتەوە. پێکەوە بۆ لاپەڕەبەندی بەکار دێن.

List the security problems of Lab 7's original insert code.کێشە ئاسایشییەکانی کۆدی زیادکردنی ئەسڵیی تاقیگەی 7 ڕیز بکە.

SQL injection (values pasted into the SQL), SHA-1 password hashing called "encryption", and mysql_error(), which no longer exists. Fix: prepared statement, password_hash(), try/catch.

(نرخەکان دەخرێنە ناو SQLـەوە)، بە ⁦SHA-1⁩ کە ناوی نراوە «» و mysql_error() کە ئیتر بوونی نییە. چارەسەر: ، password_hash()، try/catch.

Videoڤیدیۆ

Watch a videoسەیری ڤیدیۆیەک بکە

A video that viewers liked a lot and praised in their comments, chosen for this lesson.ڤیدیۆیەک کە بینەران زۆریان پێ خۆش بووە و لە کۆمێنتەکانیاندا ستایشیان کردووە، بۆ ئەم وانەیە هەڵبژێردراوە.

PDO Crash Course (PHP)Traversy Media · Englishئینگلیزی · 293K viewsبینین · 6.3K likesلایک · Partبەشی 13:35–45:35 (ofلە 45:35)

See named placeholders, then the four CRUD operations written with PDO prepared statements: fetch one row, insert, update and delete.

سەیری ناودار بکە، پاشان چوار کارەکەی کە بە نووسراون: یەک ، زیادکردن، گۆڕین و سڕینەوە.

From 13:35: queries, prepared statements, row count, INSERT and LIMIT with PDO

لە ⁦13:35⁩ بەدواوە: ، ، ژمارەی ، و بە .

Lesson complete!وانەکە تەواو بوو!

Well done. Review the key terms, then go on to the next lesson.دەستخۆش. سەیرێکی زاراوە سەرەکییەکان بکەرەوە، پاشان بڕۆ بۆ وانەی داهاتوو.

Key termsزاراوە سەرەکییەکان

Next lessonوانەی داهاتوو

Securing Data in PHP and MySQLپاراستنی دراوە لە PHP و MySQL

References and further readingسەرچاوەکان و خوێندنەوەی زیاتر

This lecture is a first edition. The Kurdish text and terminology are waiting for review by Kurdish IT teachers.ئەم وانەیە چاپی یەکەمە. دەقە کوردییەکە و زاراوەکانی چاوەڕێی پێداچوونەوەی مامۆستایانی کوردی بواری IT ن.