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

Building a CRUD Application with PHP and MySQLدروستکردنی بەرنامەیەکی CRUD بە PHP و MySQL

Create, Read, Update and Delete: the five files of the course's user-management application (config, create, view, update, delete), explained line by line, corrected and made secure, with the students' own CRUD projects as find-the-bugs exercises.

دروستکردن، خوێندنەوە، نوێکردنەوە و سڕینەوە: پێنج فایلەکەی بەرنامەی بەڕێوەبردنی بەکارهێنەرانی کۆرسەکە (config، create، view، update، delete)، دێڕ بە دێڕ ڕوونکراوەتەوە، ڕاستکراوەتەوە و سەلامەت کراوە، لەگەڵ پڕۆژەکانی CRUDـی خودی قوتابیان وەک ڕاهێنانی دۆزینەوەی باگ.

41 min read35 خولەک خوێندنەوە · 24 stepsهەنگاو

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

  • Explain what CRUD and persistent storage are, and map each operation to SQL, a page and an HTTP method.
  • Build config.php, create.php, view.php, update.php and delete.php with MySQLi prepared statements.
  • Use POST for every change, Post/Redirect/Get with header() and exit, and escape all output.
  • Find and fix bugs and security holes in real student CRUD code.
  • ڕوونی بکەیتەوە CRUD و چین و هەر کارێک ببەستیتەوە بە SQL و لاپەڕەیەک و .
  • ⁦config.php⁩ و ⁦create.php⁩ و ⁦view.php⁩ و ⁦update.php⁩ و ⁦delete.php⁩ بە MySQLi دروست بکەیت.
  • POST بۆ هەموو گۆڕانکارییەک، ⁦Post/Redirect/Get⁩ بە header() و exit بەکاربهێنیت و هەموو دەرخستەیەک بێزیان بکەیت.
  • باگ و کونی ئاسایشی لە کۆدی ڕاستەقینەی CRUDـی قوتابیاندا بدۆزیتەوە و چاکیان بکەیت.

What CRUD isCRUD چییە

Lecture 8 (PHP MySQL (CRUD Operation)) and Lab 8 (PHP and MySQL CRUD Operations) build one small application together: a list of users that you can add to, read, edit and delete. The lecture shows the code of config.php and delete.php; the lab shows create.php, view.php and update.php and asks you to write delete in class. This lecture puts the two halves together, explains every line, and turns the code into a version that is safe to put online.

وانەی ٨ (⁦PHP MySQL (CRUD Operation)⁩) و تاقیگەی ٨ (⁦PHP and MySQL CRUD Operations⁩) پێکەوە یەک بەرنامەی بچووک دروست دەکەن: لیستێکی بەکارهێنەران کە دەتوانیت زیادی بکەیت، بیخوێنیتەوە، دەستکاری بکەیت و بیسڕیتەوە. وانەکە کۆدی config.php و delete.php پیشان دەدات؛ تاقیگەکە create.php و view.php و update.php پیشان دەدات و داوات لێدەکات سڕینەوە لە پۆلدا بنووسیت. ئەم وانەیە دوو نیوەکە پێکەوە دادەنێت، هەموو دێڕێک ڕوون دەکاتەوە و کۆدەکە دەکات بە وەشانێک کە بۆ سەر ئینتەرنێت سەلامەت بێت.

Lecture 8's outline lists: what CRUD is, the importance of CRUD in PHP, and how to create the PHP–MySQL database connection. It then also covers the four operation pages, which are all here.

نەخشەی وانەی ٨ ئەمانە دەژمێرێت: CRUD چییە، گرنگیی CRUD لە PHPـدا و چۆنیەتی دروستکردنی PHP و MySQL. پاشان چوار لاپەڕەی کارەکانیش دەگرێتەوە، کە هەموویان لێرەن.

Definitionپێناسە

The four basic actions on stored data: Create, Read, Update and Delete. In SQL they are INSERT, SELECT, UPDATE and DELETE.

چوار کارە بنەڕەتییەکەی سەر دراوەی خەزنکراو: دروستکردن ⁦(Create)⁩، خوێندنەوە ⁦(Read)⁩، نوێکردنەوە ⁦(Update)⁩ و سڕینەوە ⁦(Delete)⁩. لە SQLـدا بریتین لە INSERT، SELECT، UPDATE و DELETE.

Lecture 8 defines CRUD as "a computer programming acronym that stands for create, read, update, and delete", the four functions "that are required to develop a persistent storage application". Put simply, they are the four basic actions that database and web applications use to work with data.

وانەی ٨ CRUD وا پێناسە دەکات: «کورتکراوەیەکی بەرنامەسازیی کۆمپیوتەرە بۆ create و read و update و delete»، ئەو چوار کارەی «کە بۆ گەشەپێدانی بەرنامەیەکی پێویستن». بە سادەیی، ئەوان چوار کارە بنەڕەتییەکەن کە بەرنامەکانی بنکەدراوە و وێب بۆ کارکردن لەگەڵ دراوە بەکاریان دەهێنن.

The four CRUD operations, their SQL commands (Lecture 7) and the pages of Lab 8.چوار کارەکەی CRUD، فەرمانەکانی SQLیان (وانەی ٧) و لاپەڕەکانی تاقیگەی ٨.
CRUDSQL commandفەرمانی SQLPage in our appلاپەڕە لە بەرنامەکەمانداHTTP method
Create: add a new rowCreate (دروستکردن): ڕیزێکی نوێ زیاد بکەINSERTcreate.phpPOST (the form)POST (فۆرمەکە)
Read: show rowsRead (خوێندنەوە): ڕیزەکان پیشان بدەSELECTview.phpGET
Update: change a rowUpdate (نوێکردنەوە): ڕیزێک بگۆڕەUPDATEupdate.phpGET to load the form, POST to saveGET بۆ هێنانی فۆرمەکە، POST بۆ پاشەکەوتکردن
Delete: remove a rowDelete (سڕینەوە): ڕیزێک لاببەDELETEdelete.phpPOST (a button, never a plain link)POST (دوگمەیەک، هەرگیز بەستەرێکی ئاسایی نا)
The class registerتۆماری پۆل

Think of a teacher's class register. Writing a new student's name is create. Reading the list during roll call is read. Correcting a misspelled name is update. Crossing out a student who moved to another school is delete.

تۆماری پۆلی مامۆستایەک بهێنە بەرچاو. نووسینی ناوی قوتابییەکی نوێ createـە. خوێندنەوەی لیستەکە لە کاتی ئامادەبووندا readـە. ڕاستکردنەوەی ناوێکی بە هەڵە نووسراو updateـە. هێڵکێشان بەسەر ناوی قوتابییەکدا کە گواستراوەتەوە بۆ قوتابخانەیەکی تر deleteـە.

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

A paper register has no rules about who may write in it. In a web application you must add those rules yourself.

تۆمارێکی کاغەزی هیچ یاسایەکی نییە دەربارەی ئەوەی کێ دەتوانێت تێیدا بنووسێت. لە بەرنامەیەکی وێبدا دەبێت خۆت ئەو یاسایانە زیاد بکەیت.

Quick checkپشکنینی خێراWhich SQL command does the U in CRUD use?پیتی U لە CRUDـدا کام فەرمانی SQL بەکاردەهێنێت؟

What CRUD isCRUD چییە

Persistent storageخەزنکردنی هەمیشەیی

Definitionپێناسە

Storage that keeps data after the program stops or the computer turns off, such as a or a file. A , by contrast, disappears when the script ends.

ئەو شێوەی خەزنکردنەیە کە دراوەکان دەپارێزێت تەنانەت دوای وەستانی بەرنامەکە یان کوژاندنەوەی کۆمپیوتەرەکە، وەک یان فایل. بە پێچەوانەوە، کاتێک کۆدەکە تەواو دەبێت لە ناو دەچێت.

A persistent storage application keeps data for a long time, even after the application or the computer is shut down or restarted. That is different from temporary storage such as RAM or a cache, which forgets everything when the power goes off. Persistent storage can hold files, images, videos and other kinds of data, and the data stays available even after system failures or other problems.

بەرنامەیەکی دراوە بۆ ماوەیەکی درێژ هەڵدەگرێت، تەنانەت دوای داخستن یان دووبارە هەڵکردنەوەی بەرنامەکە یان کۆمپیوتەرەکە. ئەمە جیاوازە لە خەزنکردنی کاتی وەک RAM یان cache، کە کاتێک کارەبا دەبڕێت هەموو شتێک لەبیر دەکات. دەتوانێت فایل، وێنە، ڤیدیۆ و جۆرەکانی تری دراوە هەڵبگرێت و دراوەکان تەنانەت دوای تێکچوونی سیستەم یان کێشەی تریش بەردەست دەمێننەوە.

  • Database systems, such as MySQL and SQLite: data in tables of rows and columns. Our CRUD application uses one.
  • File systems: ordinary folders and files on a disk.
  • Object storage systems: they keep huge amounts of unstructured data that is written once and read once or many times, for example the photos and videos on Facebook, the songs on Spotify, and the files in online services such as Dropbox.
  • سیستەمەکانی ، وەک MySQL و SQLite: دراوە لە خشتەی ڕیز و ستووندا. بەرنامە CRUDـەکەمان یەکێکیان بەکاردەهێنێت.
  • : فۆڵدەر و فایلی ئاسایی لەسەر دیسکێک.
  • سیستەمەکانی هەڵگرتنی ئۆبجێکت ⁦(object storage)⁩: بڕێکی زۆر لە دراوەی بێ‌ڕێکخستن هەڵدەگرن کە یەک جار دەنووسرێت و یەک جار یان زۆر جار دەخوێنرێتەوە، بۆ نموونە وێنە و ڤیدیۆکانی Facebook، گۆرانییەکانی Spotify و فایلەکانی خزمەتگوزارییە ئۆنلاینەکانی وەک Dropbox.

What CRUD isCRUD چییە

Why CRUD mattersبۆچی CRUD گرنگە

CRUD matters in PHP because it lets web applications work with data. Almost every web application is built from these four operations on data stored in a database or another persistent store. Lecture 8 gives one more reason, which it calls operation security control:

CRUD لە PHPـدا گرنگە چونکە ڕێگە بە دەدات کار لەگەڵ دراوەدا بکەن. نزیکەی هەموو لەم چوار کارە دروستکراوە لەسەر دراوەیەک کە لە بنکەدراوە یان شوێنێکی تری هەمیشەییدا هەڵگیراوە. وانەی ٨ هۆکارێکی تریش دەدات، کە ناوی دەنێت کۆنترۆڵی ئاسایشی کارەکان:

  • Fine-grained access control: because each operation is separate, you can allow one and forbid another, for example by the user's role or permission level.
  • Example 1, user data: users may read and write their own data, but may not change other users' data without proper .
  • Example 2, a finance application: only authorised users may see sensitive data such as income. Limiting who can read, write or delete keeps the data secure and correct (its integrity).
  • کۆنترۆڵی ورد بەسەر دەستڕاگەیشتندا: لەبەر ئەوەی هەر کارێک جیایە، دەتوانیت ڕێگە بە یەکێکیان بدەیت و ئەوی تر قەدەغە بکەیت، بۆ نموونە بەپێی ڕۆڵ یان ئاستی مۆڵەتی بەکارهێنەر.
  • نموونەی ١، دراوەی بەکارهێنەر: بەکارهێنەران دەتوانن دراوەی خۆیان بخوێننەوە و بنووسن، بەڵام ناتوانن بەبێ دروست دراوەی بەکارهێنەرانی تر بگۆڕن.
  • نموونەی ٢، بەرنامەیەکی دارایی: تەنها بەکارهێنەرانی ڕێگەپێدراو دەتوانن دراوەی هەستیار وەک داهات ببینن. سنووردارکردنی ئەوەی کێ دەتوانێت بخوێنێتەوە، بنووسێت یان بسڕێتەوە، دراوەکان سەلامەت و دروست دەهێڵێتەوە (تەواوێتیـیان).

The application: five filesبەرنامەکە: پێنج فایل

The application has one table, users, and five PHP files. Every page includes config.php to get the database connection. view.php is the start page; the others are reached from it.

بەرنامەکە یەک خشتەی هەیە، users و پێنج فایلی PHP. هەموو لاپەڕەیەک config.php هاوردە دەکات بۆ وەرگرتنی . view.php لاپەڕەی سەرەتایە؛ ئەوانی تر لێیەوە دەگەیەنرێن.

Page map: view.php in the middle shows the users table. An Add a user link (GET) goes to create.php, which inserts and redirects back. An Edit link with ?id=N (GET) goes to update.php, which loads a pre-filled form and, on POST, updates and redirects back. A Delete button sends a POST with the id to delete.php, which deletes and redirects back. A small users table shows id, firstname, email and hashed passwords. config.php at the bottom is included by every page and opens the connection.نەخشەی لاپەڕەکان: view.php لە ناوەڕاستدا خشتەی بەکارهێنەران پیشان دەدات. بەستەری زیادکردن (GET) دەچێت بۆ create.php، کە زیاد دەکات و دەگەڕێتەوە. بەستەری Edit بە ?id=N (GET) دەچێت بۆ update.php، کە فۆرمێکی پڕکراوە دەهێنێت و، بە POST، نوێ دەکاتەوە و دەگەڕێتەوە. دوگمەی Delete بە POST id دەنێرێت بۆ delete.php، کە دەسڕێتەوە و دەگەڕێتەوە. خشتەیەکی بچووکی users ستوونەکانی id و firstname و email و تێپەڕەوشەی هاشکراو پیشان دەدات. config.php لە خوارەوە لە هەموو لاپەڕەیەکدا هاوردە دەکرێت و پەیوەندییەکە دەکاتەوە.
The CRUD page map: solid arrows are what the user clicks, dashed arrows are redirects back to the list.نەخشەی لاپەڕەکانی CRUD: تیرە پڕەکان ئەوەن کە بەکارهێنەر کرتەی لێدەکات، تیرە پچڕاوەکان ئاڕاستەکردنەوەن بۆ لیستەکە.CêIT

How data travels between the pages

دراوە چۆن لە نێوان لاپەڕەکاندا دەڕوات

  • Form fields → $_POST: the Signup form and the update form send their fields with POST.
  • Link query strings → $_GET: the Edit link update.php?id=3 gives $_GET['id'].
  • A hidden input carries the id from the page that shows the form (GET) to the request that saves it (POST).
  • خانەکانی فۆرم ← $_POST: فۆرمی خۆتۆمارکردن و فۆرمی نوێکردنەوە خانەکانیان بە POST دەنێرن.
  • بەستەر ← $_GET: بەستەری Editـی update.php?id=3 نرخی $_GET['id'] دەدات.
  • خانەیەکی شاراوە id دەگوازێتەوە لەو لاپەڕەیەی فۆرمەکە پیشان دەدات ⁦(GET)⁩ بۆ ئەو داواکارییەی پاشەکەوتی دەکات ⁦(POST)⁩.

The steps, as the course summary lists them

هەنگاوەکان، وەک پوختەی کۆرسەکە دەیانژمێرێت

  1. Establish a database connection (config.php).
  2. The first operation, create: add new records to an existing table.
  3. The second operation, read: display the data that already exists in the database.
  4. The third operation, update: change or modify existing data.
  5. The last operation, delete: remove an existing entry.
  1. دروست بکە (config.php).
  2. یەکەم کار، create: نوێ بۆ خشتەیەکی هەبوو زیاد بکە.
  3. کاری دووەم، read: ئەو دراوانە پیشان بدە کە پێشتر لە بنکەدراوەکەدا هەن.
  4. کاری سێیەم، update: دراوەی هەبوو بگۆڕە یان دەستکاری بکە.
  5. کاری کۆتایی، delete: تۆمارێکی هەبوو لاببە.

The table

خشتەکە

Neither the lecture nor the lab shows how the mydb database and the users table are made. This is a table that fits all the pages (run it in phpMyAdmin's SQL tab):

نە وانەکە و نە تاقیگەکە پیشانی نادەن بنکەدراوەی mydb و خشتەی users چۆن دروست دەکرێن. ئەمە خشتەیەکە کە لەگەڵ هەموو لاپەڕەکان دەگونجێت (لە تابی SQLـی phpMyAdmin جێبەجێی بکە):

The users tableخشتەی usersschema.sqlSQLRuns in the databaseلە ناو بنکەدراوە کار دەکات
CREATE DATABASE mydb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE mydb;

CREATE TABLE users (
  id        INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
  firstname VARCHAR(50)  NOT NULL,
  lastname  VARCHAR(50)  NOT NULL,
  email     VARCHAR(255) NOT NULL UNIQUE,
  password  VARCHAR(255) NOT NULL,   -- holds a password_hash() result, never the real password
  gender    ENUM('Male', 'Female') NOT NULL
);
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 1

utf8mb4 stores every language, including Kurdish.

utf8mb4 هەموو زمانێک هەڵدەگرێت، لەوانە کوردی.

Lineدێڕی 5

AUTO_INCREMENT PRIMARY KEY: MySQL gives each new row the next id.

AUTO_INCREMENT PRIMARY KEY: MySQL ژمارەی دواتر دەداتە هەر ڕیزێکی نوێ.

Lineدێڕی 8

UNIQUE: two users cannot have the same email address.

UNIQUE: دوو بەکارهێنەر ناتوانن هەمان ئیمەیڵیان هەبێت.

Lineدێڕی 9

255 characters, because it holds a password_hash() result, never the password.

⁦255⁩ پیت، چونکە ئەنجامی password_hash() هەڵدەگرێت، هەرگیز خودی تێپەڕەوشەکە نا.

Rules for every page

یاساکان بۆ هەموو لاپەڕەیەک

  • Every query that contains input is a .
  • Every id is validated as a whole number.
  • Passwords are stored with password_hash() and are never shown or pre-filled.
  • Every value printed into HTML goes through htmlspecialchars() (our helper e()).
  • Anything that changes data uses POST; after it the page redirects with header() and stops with exit.
  • Errors are logged; visitors see a short message.
  • هەموو پرسێک کە نێوخستەی تێدایە .
  • هەموو idـیەک وەک پشتڕاست دەکرێتەوە.
  • تێپەڕەوشە بە password_hash() هەڵدەگیرێت و هەرگیز پیشان نادرێت یان پێشوەختە پڕ ناکرێتەوە.
  • هەموو نرخێک کە لەناو HTMLـدا چاپ دەکرێت بە htmlspecialchars()دا تێدەپەڕێت (کردارە یاریدەدەرەکەمان e()).
  • هەر شتێک دراوە بگۆڕێت POST بەکاردەهێنێت؛ دوای ئەوە لاپەڕەکە بە header() ئاڕاستە دەکاتەوە و بە exit دەوەستێت.
  • هەڵەکان تۆمار دەکرێن؛ سەردانیکەران پەیامێکی کورت دەبینن.

The application: five filesبەرنامەکە: پێنج فایل

A student's page map, checkedنەخشەی لاپەڕەکانی قوتابییەک، پشکنراو

One student drew the whole application as a diagram while studying. It has a student table (username, email, password, gender, id) and these boxes, each pointing to a Connection box:

قوتابییەک لە کاتی خوێندندا هەموو بەرنامەکەی وەک وێنەیەک کێشا. خشتەیەکی studentـی تێدایە (username، email، password، gender، id) و ئەم سندوقانە، کە هەر یەکێکیان ئاماژە بە سندوقی Connection دەکات:

  • Initial page 1: Profile.php (insertion), a "non-sticky GET form", with the note "there is no ID input in the form of this page".
  • Initial page 2: View.php (HTML table only), with two actions: Edit → update.php and Delete → delete.php.
  • update.php (from the Edit action) and delete.php (from the Delete action).
  • لاپەڕەی سەرەتا ١: ⁦Profile.php⁩ (زیادکردن)، «فۆرمێکی GETـی نالکاو»، بە تێبینی «هیچ خانەیەکی ID لە فۆرمی ئەم لاپەڕەیەدا نییە».
  • لاپەڕەی سەرەتا ٢: ⁦View.php⁩ (تەنها خشتەی HTML)، بە دوو کار: Edit ← update.php و Delete ← delete.php.
  • ⁦update.php⁩ (لە کاری Edit) و ⁦delete.php⁩ (لە کاری Delete).
The diagram's Connection boxسندوقی Connection لە وێنەکەداPHP
$db_connect = mysqli_connect('localhost','root','','db');
if(!$db_connect){
    die("Connection Failed: ".mysqli_error($db_connect));
}
The diagram's View.php box (unsafe: output not escaped)سندوقی ⁦View.php⁩ لە وێنەکەدا (نەپارێزراو: دەرخستە بێزیان نەکراوە)PHP
$query = "SELECT * FROM Table_Name";
$result = mysqli_query($db_connect, $query);


while ($row = mysqli_fetch_assoc($result)) {
echo $row['col1'];
echo $row['col2'];
echo $row['col{n}'];
}
The diagram's Edit and Delete actionsکارەکانی Edit و Delete لە وێنەکەداHTML
href="update.php?id=<?php echo $row['id']; ?>
href="delete.php?id=<?php echo $row['id']; ?>

Step 1: the connection file config.phpهەنگاوی ١: فایلی پەیوەندی config.php

"To begin, establish a database connection with your PHP code. The following code functions as a link between the webpage and the database, which will store the data from the webpage. Name the file config.php."

«بۆ دەستپێکردن، بە کۆدی PHPـەکەت دروست بکە. ئەم کۆدەی خوارەوە وەک پردێک کار دەکات لە نێوان لاپەڕەکە و بنکەدراوەکەدا، کە دراوەکانی لاپەڕەکە هەڵدەگرێت. ناوی فایلەکە بنێ ⁦config.php⁩.»

Lecture 8 / Lab 8 versionوەشانی وانەی ٨ / تاقیگەی ٨config.phpPHP
<?php
$servername = "localhost";
$username = "root";
$password = "";
$dbname = "mydb";
$conn = new mysqli($servername, $username, $password, $dbname);

if ($conn->connect_error) {
    die("Connection failed: " . $conn->connect_error);
}
?>
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 2-5

Four variables: the server, the MySQL user, its password and the database name.

چوار گۆڕاو: ڕاژەکار، بەکارهێنەری MySQL، تێپەڕەوشەکەی و ناوی بنکەدراوەکە.

Lineدێڕی 6

new mysqli(...) opens the connection and selects mydb in one step (object style).

new mysqli(...) پەیوەندییەکە دەکاتەوە و mydb هەڵدەبژێرێت بە یەک هەنگاو (شێوازی ئۆبجێکت).

Lineدێڕی 8-10

If the connection failed, connect_error holds the reason and die() stops the script.

ئەگەر پەیوەندییەکە شکستی هێنا، connect_error هۆکارەکە هەڵدەگرێت و die() دەسنووسەکە ڕادەگرێت.

The other pages share this one $conn by writing include "config.php"; at the top. The course summary shows the same code with the variable $con and the database myDB.

لاپەڕەکانی تر بە نووسینی include "config.php"; لە سەرەوە هەمان $conn بەکاردەهێنن. پوختەی کۆرسەکە هەمان کۆد بە گۆڕاوی $con و بنکەدراوەی myDB پیشان دەدات.

Improved config.php (used by every page below)⁦config.php⁩ـی باشترکراو (هەموو لاپەڕەکانی خوارەوە بەکاری دەهێنن)config.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
// config.php - one shared database connection for every page (mysqli, object-oriented style)
$servername = "localhost";
$username   = "root";   // XAMPP default only; a real server needs its own low-privilege user
$password   = "";
$dbname     = "mydb";

mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT); // errors become exceptions (default since PHP 8.1)
try {
    $conn = new mysqli($servername, $username, $password, $dbname);
    $conn->set_charset("utf8mb4");          // needed for Kurdish (Arabic-script) text
} catch (mysqli_sql_exception $e) {
    error_log($e->getMessage());            // the details go to the server's error log
    exit("Database connection failed.");    // the visitor sees only a short message
}

// e() = escape a value before printing it into HTML (used by every page)
function e($value): string {
    return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 8

Make every MySQLi error an exception (already the default since PHP 8.1, but now it is clear).

هەموو هەڵەیەکی MySQLi بکە بە هەڵاوێردە (لە ⁦PHP 8.1⁩ـەوە بنەڕەتییە، بەڵام ئێستا ڕوونە).

Lineدێڕی 11

Kurdish-safe character set.

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

Lineدێڕی 13-14

Details to the server log, a short message to the visitor.

وردەکاری بۆ لۆگی ڕاژەکار، پەیامێکی کورت بۆ سەردانیکەر.

Lineدێڕی 18-20

e() escapes a value for HTML. Defining it here means every page can use it.

e() نرخێک بۆ HTML بێزیان دەکات. پێناسەکردنی لێرە واتە هەموو لاپەڕەیەک دەتوانێت بەکاری بهێنێت.

Quick checkپشکنینی خێراSince PHP 8.1, what happens when new mysqli(...) cannot connect to the database server?لە ⁦PHP 8.1⁩ـەوە، کاتێک new mysqli(...) ناتوانێت پەیوەندی بە بکات چی ڕوودەدات؟

Step 2: Create, create.phpهەنگاوی ٢: دروستکردن، create.php

"The first operation in PHP CRUD Operations, Create, is responsible for creating … new records into an existing table. To do that, first, you must write the code for the webpage to create an entry in the database. Name the file as create.php."

«یەکەم کار لە کارەکانی CRUDـی PHP، Create، بەرپرسە لە دروستکردنی … تۆماری نوێ لەناو خشتەیەکی هەبوودا. بۆ ئەوە، سەرەتا دەبێت کۆدی لاپەڕەکە بنووسیت بۆ دروستکردنی تۆمارێک لە بنکەدراوەکەدا. ناوی فایلەکە بنێ ⁦create.php⁩.»

The lab gives the page in two parts: a PHP handler and the Signup form. Read them first; problems are listed after them.

تاقیگەکە لاپەڕەکە بە دوو بەش دەدات: بەشێکی PHP بۆ وەرگرتنی فۆرمەکە و فۆرمی خۆتۆمارکردن. سەرەتا بیانخوێنەوە؛ کێشەکان دوای ئەوان نووسراون.

Lab 8 original: PHP part (insecure, for study only)ڕەسەنی تاقیگەی ٨: بەشی PHP (نەپارێزراو، تەنها بۆ خوێندن)create.php (part 1)PHP
if (isset($_POST['submit'])) {
  $first_name = $_POST['firstname'];
  $last_name = $_POST['lastname'];
  $email = $_POST['email'];
  $password = $_POST['password'];
  $gender = $_POST['gender'];
  $sql = "INSERT INTO `users`(`firstname`, `lastname`, `email`, `password`, `gender`)
  VALUES ('$first_name','$last_name','$email','$password','$gender')";
  $result = $conn->query($sql);
  if ($result == TRUE) {
    echo "New record created successfully.";
  } else {
    echo "Error:" . $sql . "<br>" . $conn->error;
  }
  $conn->close();
}
?>
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 1

Runs only when the button named submit was posted.

تەنها کاتێک کار دەکات کە دوگمەی بە ناوی submit نێردرابێت.

Lineدێڕی 2-6

Copies the five form fields into variables.

پێنج خانەکەی فۆرمەکە دەگوازێتەوە بۆ ناو گۆڕاوەکان.

Lineدێڕی 7-8

Builds the INSERT by pasting the values into the SQL text (backticks around the names).

INSERTـەکە بە لکاندنی نرخەکان بە دەقی SQLـەوە دروست دەکات (backtick بە دەوری ناوەکاندا).

Lineدێڕی 9-14

Runs it and prints a success or error message.

جێبەجێی دەکات و پەیامی سەرکەوتن یان هەڵە چاپ دەکات.

Lineدێڕی 15

Closes the connection.

پەیوەندییەکە دادەخات.

Lab 8 original: the Signup formڕەسەنی تاقیگەی ٨: فۆرمی خۆتۆمارکردنcreate.php (part 2)HTML
<!DOCTYPE html>
<html>
<body>
  <h2>Signup Form</h2>
  <form action="" method="POST">
    <fieldset>
      <legend>Personal information:</legend>
      First name:<br />
      <input type="text" name="firstname"><br />

      Last name:<br />
      <input type="text" name="lastname"><br />

      Email:<br />
      <input type="email" name="email"><br />

      Password:<br />
      <input type="password" name="password"><br />

      Gender:<br>
      <input type="radio" name="gender" value="Male">Male
      <input type="radio" name="gender" value="Female">Female
      <br /><br />
      <input type="submit" name="submit" value="submit">
    </fieldset>

  </form>
</body>
</html>
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 5

action="" sends the form back to the same page; method="POST" matches $_POST in part 1.

action="" فۆرمەکە دەنێرێتەوە بۆ هەمان لاپەڕە؛ method="POST" لەگەڵ $_POSTی بەشی ١ دەگونجێت.

Lineدێڕی 6-7

<fieldset> and <legend> group the fields under "Personal information:".

<fieldset> و <legend> خانەکان لەژێر «⁦Personal information⁩:»دا کۆدەکەنەوە.

Lineدێڕی 21-22

Both radio buttons share name="gender", so only one value is sent.

هەردوو name="gender"یان هاوبەشە، بۆیە تەنها یەک نرخ دەنێردرێت.

Lineدێڕی 24

Each input's name becomes a $_POST key: firstname, lastname, email, password, gender, submit.

nameـی هەر خانەیەک دەبێت بە کلیلێکی $_POST: firstname، lastname، email، password، gender، submit.

Step 2: Create, create.phpهەنگاوی ٢: دروستکردن، create.php

The secure create.php⁦create.php⁩ـی سەلامەت

Secure create.php⁦create.php⁩ـی سەلامەتcreate.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
require "config.php";

$message = "";
if (isset($_POST['submit'])) {
    $first_name = trim($_POST['firstname'] ?? '');
    $last_name  = trim($_POST['lastname'] ?? '');
    $email      = trim($_POST['email'] ?? '');
    $password   = $_POST['password'] ?? '';
    $gender     = $_POST['gender'] ?? '';

    // 1. Validate on the server
    if ($first_name === '' || $last_name === '' || mb_strlen($password) < 8
        || filter_var($email, FILTER_VALIDATE_EMAIL) === false
        || !in_array($gender, ['Male', 'Female'], true)) {
        $message = "Please fill in every field correctly (password: at least 8 characters).";
    } else {
        // 2. Is the email address already used?
        $stmt = $conn->prepare("SELECT id FROM users WHERE email = ?");
        $stmt->bind_param("s", $email);
        $stmt->execute();
        if ($stmt->get_result()->num_rows > 0) {
            $message = "This email address is already registered.";
        } else {
            // 3. Insert with a prepared statement; store a hash, never the password
            $hash = password_hash($password, PASSWORD_DEFAULT);
            $stmt = $conn->prepare(
                "INSERT INTO users (firstname, lastname, email, password, gender) VALUES (?, ?, ?, ?, ?)"
            );
            $stmt->bind_param("sssss", $first_name, $last_name, $email, $hash, $gender);
            $stmt->execute();

            header("Location: view.php");   // Post/Redirect/Get
            exit;                           // stop here: nothing below may run
        }
    }
}
?>
<!DOCTYPE html>
<html>
<body>
  <h2>Signup Form</h2>
  <?php if ($message !== ""): ?>
    <p style="color: red;"><?= e($message) ?></p>
  <?php endif; ?>
  <form action="" method="POST">
    <fieldset>
      <legend>Personal information:</legend>
      <label for="firstname">First name:</label><br>
      <input type="text" id="firstname" name="firstname" value="<?= e($_POST['firstname'] ?? '') ?>" required><br>

      <label for="lastname">Last name:</label><br>
      <input type="text" id="lastname" name="lastname" value="<?= e($_POST['lastname'] ?? '') ?>" required><br>

      <label for="email">Email:</label><br>
      <input type="email" id="email" name="email" value="<?= e($_POST['email'] ?? '') ?>" required><br>

      <label for="password">Password:</label><br>
      <input type="password" id="password" name="password" minlength="8" required><br>

      Gender:<br>
      <label><input type="radio" name="gender" value="Male" <?= ($_POST['gender'] ?? '') === 'Male' ? 'checked' : '' ?> required>Male</label>
      <label><input type="radio" name="gender" value="Female" <?= ($_POST['gender'] ?? '') === 'Female' ? 'checked' : '' ?>>Female</label>
      <br><br>
      <input type="submit" name="submit" value="submit">
    </fieldset>
  </form>
  <p><a href="view.php">See all users</a></p>
</body>
</html>
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 6-10

?? '' avoids warnings when a field is missing; trim() removes spaces at both ends.

?? '' ڕێگە لە ئاگادارکردنەوە دەگرێت کاتێک خانەیەک نەبێت؛ trim() بۆشایی هەردوو سەر لادەبات.

Lineدێڕی 13-15

Server-side validation: names not empty, password at least 8 characters, a real email, gender from an allow-list.

: ناو بەتاڵ نەبێت، تێپەڕەوشە لانیکەم 8 پیت، ئیمەیڵێکی ڕاست، ڕەگەز لە .

Lineدێڕی 19-23

A prepared SELECT checks whether the email is already used (the column is UNIQUE).

SELECTێکی ئامادەکراو دەپشکنێت ئایا ئیمەیڵەکە پێشتر بەکارهاتووە (ستوونەکە UNIQUEـە).

Lineدێڕی 26-31

Hash the password, then a prepared INSERT with five s (string) values.

تێپەڕەوشەکە هاش بکە، پاشان INSERTێکی ئامادەکراو بە پێنج نرخی s (زنجیرەنووسە).

Lineدێڕی 33-34

Post/Redirect/Get: go to the list and stop.

⁦Post/Redirect/Get⁩: بڕۆ بۆ لیستەکە و بوەستە.

Lineدێڕی 44

The message is escaped like everything else that is printed.

پەیامەکە وەک هەموو شتێکی تری چاپکراو بێزیان دەکرێت.

Lineدێڕی 50

Sticky value: after an error the user does not have to type it again. The password is never sticky.

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

Step 2: Create, create.phpهەنگاوی ٢: دروستکردن، create.php

Post/Redirect/Get⁦Post/Redirect/Get⁩: پاشەکەوت، ئاڕاستەکردنەوە، هێنان

After a successful POST, the page does not print "New record created successfully.". It sends a with header("Location: view.php") and stops with exit. The browser then loads the list with a normal GET. If the user presses refresh, only that harmless GET is repeated.

دوای POSTێکی سەرکەوتوو، لاپەڕەکە «⁦New record created successfully⁩.» چاپ ناکات. بە header("Location: view.php") دەنێرێت و بە exit دەوەستێت. پاشان تۆڕگەڕەکە لیستەکە بە GETێکی ئاسایی دەهێنێت. ئەگەر بەکارهێنەر لاپەڕەکە نوێ بکاتەوە، تەنها ئەو GETـە بێزیانە دووبارە دەبێتەوە.

Sequence diagram: 1, the browser sends POST create.php with the form data and PHP inserts the row; 2, the server answers 302 Found with Location: view.php and no page; 3, the browser sends GET view.php by itself; 4, the server answers 200 OK with the list of users. A note says that refresh now repeats only step 3, while without the redirect the browser would send the POST again and insert the user twice.وێنەی ڕیزبەندی: ١، تۆڕگەڕ POST create.php بە دراوەکانی فۆرمەوە دەنێرێت و PHP ڕیزەکە زیاد دەکات؛ ٢، ڕاژەکار بە 302 Found و Location: view.php وەڵام دەداتەوە، بێ لاپەڕە؛ ٣، تۆڕگەڕ خۆی GET view.php دەنێرێت؛ ٤، ڕاژەکار بە 200 OK و لیستی بەکارهێنەران وەڵام دەداتەوە. تێبینییەک دەڵێت ئێستا نوێکردنەوە تەنها هەنگاوی ٣ دووبارە دەکاتەوە، بەڵام بەبێ ئاڕاستەکردنەوە تۆڕگەڕ POSTـەکە دووبارە دەنێرێت و بەکارهێنەرەکە دوو جار زیاد دەکات.
Post/Redirect/Get: change data with POST, then redirect to a page that is read with GET.⁦Post/Redirect/Get⁩: دراوە بە POST بگۆڕە، پاشان ئاڕاستە بکەرەوە بۆ لاپەڕەیەک کە بە GET دەخوێنرێتەوە.CêIT
Quick checkپشکنینی خێراAfter a successful POST, create.php redirects to view.php. What happens when the user then presses refresh?دوای POSTێکی سەرکەوتوو، create.php دەکات بۆ view.php. کاتێک بەکارهێنەر دواتر نوێکردنەوەی لاپەڕە ⁦(refresh)⁩ دادەگرێت چی ڕوودەدات؟

Step 3: Read, view.phpهەنگاوی ٣: خوێندنەوە، view.php

"The second operation, just as the name suggests, 'Read' is used to display or read the data that is already available in the database. To perform the operation, you need to create a page that displays the records from the table 'users'. Now, name the page as view.php."

«کاری دووەم، وەک لە ناوەکەیدا دیارە، 'Read' بۆ پیشاندان یان خوێندنەوەی ئەو دراوانە بەکاردێت کە پێشتر لە بنکەدراوەکەدا هەن. بۆ ئەنجامدانی ئەم کارە، پێویستە لاپەڕەیەک دروست بکەیت کە تۆمارەکانی خشتەی 'users' پیشان بدات. ئێستا ناوی لاپەڕەکە بنێ ⁦view.php⁩.»

Lab 8 original: run the SELECTڕەسەنی تاقیگەی ٨: جێبەجێکردنی SELECTview.php (part 1)PHP
<?php
include "config.php";
$sql = "SELECT * FROM users";
$result = $conn->query($sql);
?>
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 3

Selects every column of every row of users.

هەموو ستوونەکانی هەموو ڕیزەکانی users هەڵدەبژێرێت.

Lineدێڕی 4

$result is a mysqli_result object; the HTML below loops over it.

$result ئۆبجێکتێکی mysqli_resultـە؛ HTMLـەکەی خوارەوە بەسەریدا دەخولێتەوە.

Lab 8 original: the table with Bootstrapڕەسەنی تاقیگەی ٨: خشتەکە بە Bootstrapview.php (part 2)HTML
<!DOCTYPE html>
<html>
<head>
  <title>View Page</title>
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/css/bootstrap.min.css">
</head>
<body>
  <div class="container">
    <h2>users</h2>
    <table class="table">
      <thead>
        <tr>
          <th>ID</th>
          <th>First Name</th>
          <th>Last Name</th>
          <th>Email</th>
          <th>Gender</th>
          <th>Action</th>
        </tr>
      </thead>
      <tbody>
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 5

Loads the Bootstrap 3 stylesheet from a CDN.

شێوازنامەی ⁦Bootstrap 3⁩ لە CDNـێکەوە دەهێنێت.

Lineدێڕی 8-10

class="container" centres the page; class="table" styles the table.

class="container" لاپەڕەکە دەخاتە ناوەڕاست؛ class="table" شێوە دەداتە خشتەکە.

Lineدێڕی 13-18

Six column headings: ID, First Name, Last Name, Email, Gender, Action.

شەش سەردێڕی ستوون: ID، ⁦First Name⁩، ⁦Last Name⁩، Email، Gender، Action.

Lab 8 original: one table row per user (insecure: output not escaped)ڕەسەنی تاقیگەی ٨: ڕیزێکی خشتە بۆ هەر بەکارهێنەرێک (نەپارێزراو: دەرخستە بێزیان نەکراوە)view.php (part 3)PHP
<?php
          if ($result->num_rows > 0) {
            while ($row = $result->fetch_assoc()) {
              ?>
              <tr>
                <td><?php echo $row['id']; ?></td>
                <td><?php echo $row['firstname']; ?></td>
                <td> <?php echo $row['lastname']; ?></td>
                <td> <?php echo $row['email']; ?> </td>
                <td><?php echo $row['gender']; ?></td>
                <td><a class="btn btn-info" href="update.php?id=<?php echo $row['id']; ?>">Edit</a>&nbsp;<a
                    class="btn btn-danger" href="delete.php?id=<?php echo $row['id']; ?>">Delete</a></td>
              </tr>
            <?php }}?>
      </tbody>
    </table>
  </div>
</body>
</html>
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 2

num_rows is the number of rows returned. Nothing is printed if it is 0.

num_rows ژمارەی ئەو ڕیزانەیە کە گەڕاونەتەوە. ئەگەر 0 بێت هیچ چاپ ناکرێت.

Lineدێڕی 3

fetch_assoc() returns one row as an associative array each time, until there are no rows left.

fetch_assoc() هەر جارە یەک ڕیز وەک دەگەڕێنێتەوە، تا هیچ ڕیزێک نەمێنێت.

Lineدێڕی 4-10

PHP is closed with ?> so that plain HTML can be written; small <?php echo ... ?> islands print each column.

PHP بە ?> دادەخرێت بۆ ئەوەی HTMLـی ئاسایی بنووسرێت؛ دوورگەی بچووکی <?php echo ... ?> هەر ستوونێک چاپ دەکەن.

Lineدێڕی 11-12

Two Bootstrap buttons: Edit (btn btn-info) → update.php?id=N, Delete (btn btn-danger) → delete.php?id=N, separated by &nbsp;.

دوو دوگمەی ⁦Bootstrap: Edit⁩ (btn btn-info) ← update.php?id=N، Delete (btn btn-danger) ← delete.php?id=N، کە بە &nbsp; جیاکراونەتەوە.

Lineدێڕی 14

}} closes the while and then the if.

}} یەکەم while و پاشان if دادەخات.

This mixing of PHP and HTML is the key idea of the Read page: PHP repeats a block of HTML once for every row.

ئەم تێکەڵکردنەی PHP و HTML بیرۆکەی سەرەکیی لاپەڕەی Readـە: PHP بلۆکێکی HTML بۆ هەر ڕیزێک جارێک دووبارە دەکاتەوە.

Step 3: Read, view.phpهەنگاوی ٣: خوێندنەوە، view.php

The secure view.php⁦view.php⁩ـی سەلامەت

Secure view.php⁦view.php⁩ـی سەلامەتview.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
require "config.php";

// No user input in this query, so a plain query() is safe here
$result = $conn->query("SELECT id, firstname, lastname, email, gender FROM users ORDER BY id");
?>
<!DOCTYPE html>
<html>
<head>
  <title>View Page</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css">
</head>
<body>
  <div class="container">
    <h2>users</h2>
    <p><a class="btn btn-primary" href="create.php">Add a user</a></p>
    <table class="table">
      <thead>
        <tr><th>ID</th><th>First Name</th><th>Last Name</th><th>Email</th><th>Gender</th><th>Action</th></tr>
      </thead>
      <tbody>
      <?php if ($result->num_rows === 0): ?>
        <tr><td colspan="6">No users yet.</td></tr>
      <?php endif; ?>
      <?php while ($row = $result->fetch_assoc()): ?>
        <tr>
          <td><?= e($row['id']) ?></td>
          <td><?= e($row['firstname']) ?></td>
          <td><?= e($row['lastname']) ?></td>
          <td><?= e($row['email']) ?></td>
          <td><?= e($row['gender']) ?></td>
          <td>
            <a class="btn btn-info" href="update.php?id=<?= e($row['id']) ?>">Edit</a>
            <form action="delete.php" method="post" style="display:inline"
                  onsubmit="return confirm('Delete this user?');">
              <input type="hidden" name="id" value="<?= e($row['id']) ?>">
              <button type="submit" class="btn btn-danger">Delete</button>
            </form>
          </td>
        </tr>
      <?php endwhile; ?>
      </tbody>
    </table>
  </div>
</body>
</html>
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 5

Only the columns we show, in a fixed order. No input is used, so query() is fine.

تەنها ئەو ستوونانەی پیشانیان دەدەین، بە ڕیزبەندییەکی جێگیر. هیچ نێوخستەیەک بەکارنەهاتووە، بۆیە query() باشە.

Lineدێڕی 22-24

A friendly row when the table is empty.

ڕیزێکی دۆستانە کاتێک خشتەکە بەتاڵە.

Lineدێڕی 25

while (...): … endwhile; is easier to read inside HTML than { … }.

while (...): … endwhile; لەناو HTMLـدا لە { … } ئاسانتر دەخوێنرێتەوە.

Lineدێڕی 27-31

<?= ... ?> is short for <?php echo ... ?>; every value goes through e().

<?= ... ?> کورتکراوەی <?php echo ... ?>ـە؛ هەموو نرخێک بە e()دا تێدەپەڕێت.

Lineدێڕی 34-38

Delete is a POST form with a hidden id and a JavaScript confirm() question.

Delete فۆرمێکی POSTـە بە idـیەکی شاراوە و پرسیارێکی confirm()ـی JavaScript.

Quick checkپشکنینی خێراIn view.php, what does $result->fetch_assoc() return when there are no rows left?لە view.phpـدا، $result->fetch_assoc() چی دەگەڕێنێتەوە کاتێک هیچ ڕیزێک نەماوە؟

Step 4: Update, update.phpهەنگاوی ٤: نوێکردنەوە، update.php

"The third operation, i.e., 'update' is used to change or modify the already existing data present in the database. To do this, you need to create another page to update the details in the database. Here, name the page as update.php."

«کاری سێیەم، واتە 'update'، بۆ گۆڕین یان دەستکاریکردنی ئەو دراوانە بەکاردێت کە پێشتر لە بنکەدراوەکەدا هەن. بۆ ئەمە، پێویستە لاپەڕەیەکی تر دروست بکەیت بۆ نوێکردنەوەی وردەکارییەکان لە بنکەدراوەکەدا. لێرەدا ناوی لاپەڕەکە بنێ ⁦update.php⁩.»

update.php does two jobs. Opened from the Edit link (update.php?id=3, a GET request), it loads that user and shows a form already filled with the current values. When that form is submitted (POST, to the same address), it saves the changes. The lab gives it in four parts.

update.php دوو کار دەکات. کاتێک لە بەستەری Edit کراوە دەبێت (update.php?id=3، داواکارییەکی GET)، ئەو بەکارهێنەرە دەهێنێت و فۆرمێک پیشان دەدات کە پێشتر بە نرخە ئێستاکان پڕکراوەتەوە. کاتێک ئەو فۆرمە دەنێردرێت (POST، بۆ هەمان ناونیشان)، گۆڕانکارییەکان پاشەکەوت دەکات. تاقیگەکە بە چوار بەش دەیدات.

Lab 8 original: save the changes (POST) (insecure, for study only)ڕەسەنی تاقیگەی ٨: پاشەکەوتکردنی گۆڕانکارییەکان ⁦(POST)⁩ (نەپارێزراو، تەنها بۆ خوێندن)update.php (part 1)PHP
<?php
include "config.php";

   if (isset($_POST['update'])) {
       $firstname = $_POST['firstname'];
       $user_id = $_POST['user_id'];
       $lastname = $_POST['lastname'];
       $email = $_POST['email'];
       $password = $_POST['password'];
       $gender = $_POST['gender'];
       $sql = "UPDATE `users` SET `firstname`='$firstname',`lastname`='$lastname',
               `email`='$email',`password`='$password',`gender`='$gender' WHERE `id`='$user_id'";
       $result = $conn->query($sql);

       if ($result == TRUE) {
           echo "Record updated successfully.";
       }else{
           echo "Error:" . $sql . "<br>" . $conn->error;
       }  }
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 4

Runs when the submit button named update was posted.

کاتێک کار دەکات کە بە ناوی update نێردرابێت.

Lineدێڕی 6

user_id comes from a hidden input in the form.

user_id لە خانەیەکی شاراوەی فۆرمەکەوە دێت.

Lineدێڕی 11-12

Builds UPDATE … SET every column … WHERE id = user_id by pasting values.

UPDATE … SET هەموو ستوونێک … WHERE id = user_id بە لکاندنی نرخەکان دروست دەکات.

Lab 8 original: load the user (GET) (insecure, for study only)ڕەسەنی تاقیگەی ٨: هێنانی بەکارهێنەرەکە ⁦(GET)⁩ (نەپارێزراو، تەنها بۆ خوێندن)update.php (part 2)PHP
if (isset($_GET['id'])) {
  $user_id = $_GET['id'];
  $sql = "SELECT * FROM `users` WHERE `id`='$user_id'";
  $result = $conn->query($sql);


  if ($result->num_rows > 0) {
     while ($row = $result->fetch_assoc()) {
       $first_name = $row['firstname'];
       $lastname = $row['lastname'];
       $email = $row['email'];
       $password  = $row['password'];
       $gender = $row['gender'];
       $id = $row['id'];
}?>
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 1-3

The Edit link supplies $_GET['id']; the user with that id is selected.

بەستەری Edit نرخی $_GET['id'] دەدات؛ بەکارهێنەری خاوەن ئەو idـیە هەڵدەبژێردرێت.

Lineدێڕی 7-14

Each column is copied into a variable for the form.

هەر ستوونێک دەگوازرێتەوە بۆ گۆڕاوێک بۆ فۆرمەکە.

Lineدێڕی 15

}?> closes only the while. The two if blocks stay open around the form and are closed in part 4.

}?> تەنها while دادەخات. دوو بلۆکی if بە دەوری فۆرمەکەدا کراوە دەمێننەوە و لە بەشی ٤دا دادەخرێن.

Lab 8 original: the pre-filled form (insecure, for study only)ڕەسەنی تاقیگەی ٨: فۆرمی پڕکراوە (نەپارێزراو، تەنها بۆ خوێندن)update.php (part 3)PHP
<h2>User Update Form</h2>
    <form action="" method="post">
     <fieldset>
      <legend>Personal information:</legend>

      First name:<br>
      <input type="text" name="firstname" value="<?php echo $first_name; ?>">

      <input type="hidden" name="user_id" value="<?php echo $id; ?>"><br />

      Last name:<br />
      <input type="text" name="lastname" value="<?php echo $lastname; ?>"><br />

      Email:<br />
      <input type="email" name="email" value="<?php echo $email; ?>"><br />

      Password:<br />
      <input type="password" name="password" value="<?php echo $password; ?>"><br />
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 2

action="" posts to the same URL, which still contains ?id=N, so after saving the GET part runs again and shows the new values under the message.

action="" بۆ هەمان URL دەنێرێت، کە هێشتا ?id=Nـی تێدایە، بۆیە دوای پاشەکەوتکردن بەشی GET دووبارە کار دەکات و نرخە نوێیەکان لەژێر پەیامەکەدا پیشان دەدات.

Lineدێڕی 7

Each value attribute shows the current value from the database.

هەر خەسڵەتێکی value نرخی ئێستای بنکەدراوەکە پیشان دەدات.

Lineدێڕی 9

The hidden input carries the id back to part 1.

خانە شاراوەکە id دەگەڕێنێتەوە بۆ بەشی ١.

Lab 8 original: radios, submit and the redirect (no exit after header())ڕەسەنی تاقیگەی ٨: دوگمەی ڕادیۆ، ناردن و ئاڕاستەکردنەوە (دوای ⁦header()⁩ هیچ exitێک نییە)update.php (part 4)PHP
Gender:<br />
      <input type="radio" name="gender" value="Male" <?php if($gender == 'Male')
      { echo "checked";} ?> >Male
      <input type="radio" name="gender" value="Female" <?php if($gender == 'Female')
      { echo "checked";} ?>>Female
      <br /><br />
      <input type="submit" value="Update" name="update">


     </fieldset>
    </form>
    </body>
    </html>
 <?php
 } else{
    header('Location: view.php');
 }}?>
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 2-5

The stored gender pre-selects its radio button by printing checked.

ڕەگەزی هەڵگیراو بە چاپکردنی checked دوگمەی ڕادیۆکەی خۆی پێشوەختە هەڵدەبژێرێت.

Lineدێڕی 7

The button named update triggers part 1.

دوگمەی بە ناوی update بەشی ١ کارا دەکات.

Lineدێڕی 14-16

} else { belongs to if (num_rows > 0): no user with that id → back to view.php. The last } closes if (isset($_GET['id'])).

} else { سەر بە if (num_rows > 0)ـە: هیچ بەکارهێنەرێک بەو idـیە نییە ← بگەڕێوە بۆ ⁦view.php⁩. دوایین } بلۆکی if (isset($_GET['id'])) دادەخات.

Step 4: Update, update.phpهەنگاوی ٤: نوێکردنەوە، update.php

The secure update.php⁦update.php⁩ـی سەلامەت

Secure update.php⁦update.php⁩ـی سەلامەتupdate.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
require "config.php";

$message = "";
// Part 1: the form was submitted (POST) - save the changes
if (isset($_POST['update'])) {
    $user_id   = filter_var($_POST['user_id'] ?? null, FILTER_VALIDATE_INT);
    $firstname = trim($_POST['firstname'] ?? '');
    $lastname  = trim($_POST['lastname'] ?? '');
    $email     = trim($_POST['email'] ?? '');
    $gender    = $_POST['gender'] ?? '';
    $password  = $_POST['password'] ?? '';

    if ($user_id === false || $user_id === null || $firstname === '' || $lastname === ''
        || filter_var($email, FILTER_VALIDATE_EMAIL) === false
        || !in_array($gender, ['Male', 'Female'], true)
        || ($password !== '' && mb_strlen($password) < 8)) {
        $message = "Please fill in every field correctly.";
    } else {
        $stmt = $conn->prepare("UPDATE users SET firstname = ?, lastname = ?, email = ?, gender = ? WHERE id = ?");
        $stmt->bind_param("ssssi", $firstname, $lastname, $email, $gender, $user_id);
        $stmt->execute();

        if ($password !== '') {                 // change the password only when a new one was typed
            $hash = password_hash($password, PASSWORD_DEFAULT);
            $stmt = $conn->prepare("UPDATE users SET password = ? WHERE id = ?");
            $stmt->bind_param("si", $hash, $user_id);
            $stmt->execute();
        }
        header("Location: view.php");           // Post/Redirect/Get
        exit;
    }
}

// Part 2: opened from the Edit link (update.php?id=N) - load that user
$user_id = filter_var($_GET['id'] ?? null, FILTER_VALIDATE_INT);
if ($user_id === false || $user_id === null) {
    header("Location: view.php");
    exit;
}
$stmt = $conn->prepare("SELECT id, firstname, lastname, email, gender FROM users WHERE id = ?");
$stmt->bind_param("i", $user_id);
$stmt->execute();
$user = $stmt->get_result()->fetch_assoc();     // one row, or null if the id does not exist
if ($user === null) {
    header("Location: view.php");
    exit;
}
?>
<!DOCTYPE html>
<html>
<body>
  <h2>User Update Form</h2>
  <?php if ($message !== ""): ?>
    <p style="color: red;"><?= e($message) ?></p>
  <?php endif; ?>
  <form action="" method="post">
    <fieldset>
      <legend>Personal information:</legend>
      <input type="hidden" name="user_id" value="<?= e($user['id']) ?>">
      First name:<br>
      <input type="text" name="firstname" value="<?= e($user['firstname']) ?>" required><br>
      Last name:<br>
      <input type="text" name="lastname" value="<?= e($user['lastname']) ?>" required><br>
      Email:<br>
      <input type="email" name="email" value="<?= e($user['email']) ?>" required><br>
      New password (leave empty to keep the old one):<br>
      <input type="password" name="password" value=""><br>
      Gender:<br>
      <input type="radio" name="gender" value="Male" <?= $user['gender'] === 'Male' ? 'checked' : '' ?>>Male
      <input type="radio" name="gender" value="Female" <?= $user['gender'] === 'Female' ? 'checked' : '' ?>>Female
      <br><br>
      <input type="submit" value="Update" name="update">
    </fieldset>
  </form>
</body>
</html>
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 7

The hidden id is validated like any other input.

idـی شاراوە وەک هەر نێوخستەیەکی تر پشتڕاست دەکرێتەوە.

Lineدێڕی 14-17

Validate everything; an empty password field is allowed (it means "keep the old one").

هەموو شتێک پشتڕاست بکەرەوە؛ بەتاڵ ڕێگەپێدراوە (واتە «کۆنەکە بهێڵەوە»).

Lineدێڕی 20-22

A prepared UPDATE; "ssssi" = four strings and one integer, in the order of the ?.

UPDATEێکی ئامادەکراو؛ "ssssi" = چوار زنجیرەنووسە و یەک ، بە ڕیزبەندی ?ـەکان.

Lineدێڕی 24-29

The password changes only if a new one was typed, and it is hashed.

تێپەڕەوشە تەنها ئەگەر نوێیەک نووسرابێت دەگۆڕێت و هاش دەکرێت.

Lineدێڕی 30-31

Post/Redirect/Get.

⁦Post/Redirect/Get⁩: ئاڕاستە بکەرەوە و بوەستە.

Lineدێڕی 36-40

Opened with a bad or missing ?id: back to the list, and stop.

بە ?idـی هەڵە یان نەبوو کراوەتەوە: بگەڕێوە بۆ لیستەکە و بوەستە.

Lineدێڕی 44

One row (or null): no while loop is needed.

یەک ڕیز (یان null): هیچ خولێکی while پێویست نییە.

Lineدێڕی 68

The password box is always empty.

هەمیشە بەتاڵە.

Quick checkپشکنینی خێراIn update.php, why is the user's id sent in a hidden input?لە update.phpـدا، بۆچی idـی بەکارهێنەر لە خانەیەکی شاراوەدا دەنێردرێت؟

Step 5: Delete, delete.phpهەنگاوی ٥: سڕینەوە، delete.php

"The last operation of CRUD is Delete and just as the name suggests, it is used to delete an existing entry or table. To perform this operation, you must create a page that would let you choose the data entry that you want to delete from the database. Now, name the file delete.php." The lab adds: "You should implement delete in the class". Lecture 8 gives this code:

«کۆتا کاری CRUD، Deleteـە و وەک لە ناوەکەیدا دیارە، بۆ سڕینەوەی تۆمارێک یان خشتەیەکی هەبوو بەکاردێت. بۆ ئەنجامدانی ئەم کارە، دەبێت لاپەڕەیەک دروست بکەیت کە ڕێگەت پێبدات ئەو تۆمارە هەڵبژێریت کە دەتەوێت لە بنکەدراوەکەدا بیسڕیتەوە. ئێستا ناوی فایلەکە بنێ ⁦delete.php⁩.» تاقیگەکە زیاد دەکات: «دەبێت سڕینەوە لە پۆلدا جێبەجێ بکەیت». وانەی ٨ ئەم کۆدە دەدات:

Lecture 8 original (insecure, for study only)ڕەسەنی وانەی ٨ (نەپارێزراو، تەنها بۆ خوێندن)delete.phpPHP
<?php
include "config.php";

if (isset($_GET['id'])) {

    $user_id = $_GET['id'];
    $sql = "DELETE FROM `users` WHERE `id`='$user_id'";
    $result = $conn->query($sql);

    if ($result == TRUE) {

        echo "Record deleted successfully.";
    } else {
        echo "Error:" . $sql . "<br>" . $conn->error;
    }
}
?>
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 2

Includes config.php to get $conn.

config.php هاوردە دەکات بۆ وەرگرتنی $conn.

Lineدێڕی 4

Runs when the URL has ?id= (from the Delete link delete.php?id=N in view.php).

کاتێک کار دەکات کە URLـەکە ?id=ـی تێدا بێت (لە بەستەری Deleteـی delete.php?id=N لە ⁦view.php⁩).

Lineدێڕی 6-8

Builds DELETE … WHERE id = that value and runs it.

DELETE … WHERE id = ئەو نرخە دروست دەکات و جێبەجێی دەکات.

Lineدێڕی 10-14

Prints "Record deleted successfully." or "Error:" with the SQL and the MySQL error.

«⁦Record deleted successfully⁩.» یان «Error:» لەگەڵ SQL و هەڵەی MySQL چاپ دەکات.

The same script in the course summary (insecure, for study only)هەمان دەسنووس لە پوختەی کۆرسەکەدا (نەپارێزراو، تەنها بۆ خوێندن)delete.php (summary)PHP
include 'connection.php';
if (isset($_GET['id']))
{
        $user_id = $_GET['id'];
        $sql = "delete from users where id='$user_id'";
        $result = $con->query($sql);
        if ($result == TRUE)
        {
                 echo "Record deleted successfully.";
        }
        else
        {
                 echo "Error: ".$sql."<br>".$con->connect_error;
        }
}

Step 5: Delete, delete.phpهەنگاوی ٥: سڕینەوە، delete.php

The secure delete.php⁦delete.php⁩ـی سەلامەت

Secure delete.php (the in-class task)⁦delete.php⁩ـی سەلامەت (ئەرکی ناو پۆل)delete.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
require "config.php";

// Deleting changes data, so accept it only from a POST form - never from a plain link
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $user_id = filter_var($_POST['id'] ?? null, FILTER_VALIDATE_INT);
    if ($user_id !== false && $user_id !== null) {
        $stmt = $conn->prepare("DELETE FROM users WHERE id = ?");
        $stmt->bind_param("i", $user_id);
        $stmt->execute();
    }
}
header("Location: view.php");
exit;
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 5

Only a POST request may delete. Opening delete.php?id=3 as a link does nothing.

تەنها داواکارییەکی POST دەتوانێت بسڕێتەوە. کردنەوەی delete.php?id=3 وەک بەستەر هیچ ناکات.

Lineدێڕی 6

The id must be a whole number; the attack text gives false.

id دەبێت بێت؛ دەقی هێرشەکە false دەدات.

Lineدێڕی 8-10

A prepared DELETE with one integer.

DELETEێکی ئامادەکراو بە یەک .

Lineدێڕی 13-14

Always go back to the list, and stop.

هەمیشە بگەڕێوە بۆ لیستەکە و بوەستە.

The Delete button that sends this POST is the small form in the secure view.php: a hidden id input and onsubmit="return confirm('Delete this user?');", which asks the user before the form is sent.

ئەو دوگمەی Deleteـەی ئەم POSTـە دەنێرێت فۆرمە بچووکەکەی ناو view.phpـی سەلامەتە: خانەیەکی شاراوەی id و onsubmit="return confirm('Delete this user?');"، کە پێش ناردنی فۆرمەکە پرسیار لە بەکارهێنەر دەکات.

Quick checkپشکنینی خێراWhy must Delete be a POST form and not a link like delete.php?id=3?بۆچی Delete دەبێت فۆرمێکی POST بێت نەک بەستەرێکی وەک delete.php?id=3؟

Cross Site Request Forgery - ComputerphileComputerphile · Englishئینگلیزی · 819K viewsبینین · 20K likesلایک

Learn how another website can trick your browser into deleting or changing data, and how a secret token in the form stops it.

فێر دەبیت چۆن تر دەتوانێت هەڵبخەڵەتێنێت تا بسڕێتەوە یان بیانگۆڕێت و چۆن کۆدێکی نهێنی لەناو ڕێگری لەمە دەکات.

Whole video; GET vs POST at 0:41, the token defence at 6:58.

هەموو ڤیدیۆکە؛ GET بەرامبەر POST لە ⁦0:41⁩ و بەرگری بە کۆدی نهێنی (token) لە ⁦6:58⁩.

The same operations with PDOهەمان کارەکان بە PDO

The course uses MySQLi, but many projects use , which works with MySQL, SQLite and other databases. Here are the four operations as small functions. Because PDO is the same for every database, they were run here on SQLite by changing only the connection line.

کۆرسەکە MySQLi بەکاردەهێنێت، بەڵام زۆر پڕۆژە بەکاردەهێنن، کە لەگەڵ MySQL و SQLite و بنکەدراوەکانی تر کار دەکات. ئەمانە چوار کارەکەن وەک کرداری بچووک. لەبەر ئەوەی PDO بۆ هەموو بنکەدراوەیەک یەکە، لێرە لەسەر SQLite کارپێکران تەنها بە گۆڕینی دێڕی پەیوەندییەکە.

config.php for PDO and MySQL⁦config.php⁩ بۆ PDO و MySQLconfig.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
// config.php - PDO version (works with MySQL; change the DSN for other databases)
$dsn = "mysql:host=localhost;dbname=mydb;charset=utf8mb4";
try {
    $pdo = new PDO($dsn, "root", "", [
        PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,  // the default since PHP 8.0
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,        // rows come back as associative arrays
        PDO::ATTR_EMULATE_PREPARES   => false,                   // real prepared statements in MySQL
    ]);
} catch (PDOException $e) {
    error_log($e->getMessage());
    exit("Database connection failed.");
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 3

The : driver, host, database and character set in one text.

: درایڤەر، ڕاژەکار، بنکەدراوە و کۆمەڵەی پیت لە یەک دەقدا.

Lineدێڕی 7

Rows come back as associative arrays, like fetch_assoc().

ڕیزەکان وەک دەگەڕێنەوە، وەک fetch_assoc().

Lineدێڕی 8

Use real prepared statements in MySQL.

لە MySQLـدا ڕاستەقینە بەکاربهێنە.

CRUD as four functionsCRUD وەک چوار کردارcrud.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
// crud.php - the four CRUD operations as functions (PDO)

function create_user(PDO $pdo, string $first, string $last, string $email, string $password, string $gender): int {
    $stmt = $pdo->prepare("INSERT INTO users (firstname, lastname, email, password, gender) VALUES (?, ?, ?, ?, ?)");
    $stmt->execute([$first, $last, $email, password_hash($password, PASSWORD_DEFAULT), $gender]);
    return (int) $pdo->lastInsertId();             // the id the database gave the new row
}

function all_users(PDO $pdo): array {
    return $pdo->query("SELECT id, firstname, lastname, email, gender FROM users ORDER BY id")->fetchAll();
}

function find_user(PDO $pdo, int $id): array|false {
    $stmt = $pdo->prepare("SELECT id, firstname, lastname, email, gender FROM users WHERE id = :id");
    $stmt->execute(['id' => $id]);
    return $stmt->fetch();                         // false when no row matches
}

function update_user(PDO $pdo, int $id, string $first, string $last, string $email, string $gender): int {
    $stmt = $pdo->prepare("UPDATE users SET firstname = ?, lastname = ?, email = ?, gender = ? WHERE id = ?");
    $stmt->execute([$first, $last, $email, $gender, $id]);
    return $stmt->rowCount();                      // number of rows changed
}

function delete_user(PDO $pdo, int $id): int {
    $stmt = $pdo->prepare("DELETE FROM users WHERE id = ?");
    $stmt->execute([$id]);
    return $stmt->rowCount();                      // number of rows deleted
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 7

lastInsertId(): the id the database gave the new row.

lastInsertId(): ئەو idـیەی بنکەدراوەکە بە ڕیزە نوێیەکەی دا.

Lineدێڕی 15

A named placeholder :id, filled from the array key 'id'.

ناودار :id، کە لە کلیلی 'id'ی پڕ دەکرێتەوە.

Lineدێڕی 17

fetch() returns false when no row matches.

fetch() کاتێک هیچ ڕیزێک نەگونجێت false دەگەڕێنێتەوە.

Lineدێڕی 23

rowCount(): how many rows were changed.

rowCount(): چەند ڕیز گۆڕدران.

Lineدێڕی 29

rowCount(): how many rows were deleted.

rowCount(): چەند ڕیز سڕدرانەوە.

Running the functions on SQLiteکارپێکردنی کردارەکان لەسەر SQLitedemo.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
// Try the same functions on SQLite: only the connection line is different
$pdo = new PDO("sqlite::memory:", null, null, [PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC]);
$pdo->exec("CREATE TABLE users (id INTEGER PRIMARY KEY, firstname TEXT NOT NULL, lastname TEXT NOT NULL,
            email TEXT NOT NULL UNIQUE, password TEXT NOT NULL, gender TEXT NOT NULL)");
require "crud.php";

$a = create_user($pdo, "Sara", "O'Brien", "sara@example.com", "sara-pass-1", "Female");
$b = create_user($pdo, "دیلان", "کەریم", "dilan@example.com", "dilan-pass-1", "Male");
echo "Created ids: $a, $b\n";

foreach (all_users($pdo) as $u) {
    echo $u['id'], ": ", $u['firstname'], " ", $u['lastname'], "\n";
}
echo "Updated rows: ", update_user($pdo, $b, "Dilan", "Karim", "dilan@example.com", "Male"), "\n";
print_r(find_user($pdo, $b));
var_dump(find_user($pdo, 99));
echo "Deleted rows: ", delete_user($pdo, $a), "\n";
echo "Users left: ", count(all_users($pdo)), "\n";
PDO Crash Course (PHP)Traversy Media · Englishئینگلیزی · 293K viewsبینین · 6.3K likesلایک · Partبەشی 26:04–38:43 (ofلە 45:35)

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

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

Watch 26:04–38:43: named placeholders, then fetch, insert, update, delete with PDO.

لە ⁦26:04⁩ تا ⁦38:43⁩ سەیر بکە: ناودار، پاشان ، زیادکردن، گۆڕین و سڕینەوە بە .

Who may do whatکێ دەتوانێت چی بکات

Lecture 8 says CRUD helps to control who can read, change or delete data. In code, that means two checks at the top of every page that shows or changes private data:

وانەی ٨ دەڵێت CRUD یارمەتی کۆنترۆڵکردنی ئەوە دەدات کە کێ دەتوانێت دراوە بخوێنێتەوە، بیگۆڕێت یان بیسڕێتەوە. لە کۆددا، ئەمە واتە دوو پشکنین لە سەرەوەی هەموو لاپەڕەیەک کە دراوەی تایبەت پیشان دەدات یان دەیگۆڕێت:

  1. : is anybody logged in? If not, redirect to the login page and exit.
  2. : may this user do this to this row? For example, a user may delete only their own notes, or only an admin may delete users.
  1. : ئایا کەسێک چووەتە ژوورەوە؟ ئەگەر نا، ئاڕاستە بکەرەوە بۆ لاپەڕەی چوونەژوورەوە و exit.
  2. : ئایا ئەم بەکارهێنەرە دەتوانێت ئەم کارە بەم ڕیزە بکات؟ بۆ نموونە، بەکارهێنەر تەنها دەتوانێت تێبینییەکانی خۆی بسڕێتەوە، یان تەنها بەڕێوەبەر دەتوانێت بەکارهێنەران بسڕێتەوە.
1. Only logged-in users١. تەنها بەکارهێنەرانی چووەژوورەوەprotect.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
session_start();                         // the very first line of the page
if (!isset($_SESSION['userId'])) {
    header('Location: login.php');
    exit;                                // without exit, the rest of the page is still sent
}

The second check can often be written into the SQL itself: add the owner to the WHERE clause. Then a user who changes the id in the form still cannot touch other people's rows.

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

2. Only your own rows٢. تەنها ڕیزەکانی خۆتowner-check.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
$pdo = new PDO("sqlite::memory:");
$pdo->exec("CREATE TABLE notes (id INTEGER PRIMARY KEY, owner_id INTEGER, text TEXT)");
$pdo->exec("INSERT INTO notes (owner_id, text) VALUES (1, 'Aram''s note'), (2, 'Hana''s note')");

function delete_note(PDO $pdo, int $note_id, int $current_user_id): int {
    // the extra condition "AND owner_id = ?" means: only your OWN notes
    $stmt = $pdo->prepare("DELETE FROM notes WHERE id = ? AND owner_id = ?");
    $stmt->execute([$note_id, $current_user_id]);
    return $stmt->rowCount();
}

echo "Hana (user 2) deletes note 1: ", delete_note($pdo, 1, 2), " row(s)\n";
echo "Aram (user 1) deletes note 1: ", delete_note($pdo, 1, 1), " row(s)\n";
Quick checkپشکنینی خێراHana changes the hidden id in a form so that it points to Aram's note. Which query stops her from deleting it?⁦Hana id⁩ـی شاراوەی ناو فۆرمێک دەگۆڕێت بۆ ئەوەی ئاماژە بە تێبینییەکەی Aram بکات. کام ڕێگەی لێدەگرێت بیسڕێتەوە؟

Find the bugs: students' CRUD projectsباگەکان بدۆزەرەوە: پڕۆژەکانی CRUDـی قوتابیان

The course's practical notes contain real CRUD pages written by students while they practised. They work on a teacher's computer, but each one has bugs and security holes. Finding them is the best way to check what you learned. Try each exercise before you open the answer.

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

Find the bugs: students' CRUD projectsباگەکان بدۆزەرەوە: پڕۆژەکانی CRUDـی قوتابیان

A. The product stock pagesA. لاپەڕەکانی کۆگای بەرهەم

ExerciseڕاهێنانA. Find the bugs: the product stock pagesA. باگەکان بدۆزەرەوە: لاپەڕەکانی کۆگای بەرهەم

A practice project by two students shows a list of products with their stock. Buy lowers the stock by one (never below zero) and Add raises it by one; both go back to product.php. Read the three files and list every bug and security problem. Then write fixed versions.

پڕۆژەیەکی ڕاهێنانی دوو قوتابی لیستێکی بەرهەمەکان لەگەڵ بڕی کۆگاکەیان پیشان دەدات. Buy کۆگاکە یەکێک کەم دەکاتەوە (هەرگیز لە سفر کەمتر نا) و Add یەکێک زیادی دەکات؛ هەردووکیان دەگەڕێنەوە بۆ product.php. سێ فایلەکە بخوێنەوە و هەموو باگێک و کێشەیەکی ئاسایشی بنووسە. پاشان وەشانی چاککراو بنووسە.

Original product.php (with bugs: for study only)⁦product.php⁩ـی ڕەسەن (بە باگەوە: تەنها بۆ خوێندن)product.phpPHP
<?php

$connect = mysqli_connect('localhost', 'root', '', 'MyStudy');

if (!$connect) {
    die(mysqli_error($connect));
}

?>

<!DOCTYPE html>
<html lang="en">

<body>


    <table border="6">
        <tr>
            <th>ID</th>
            <th>PRODUCT NAME</th>
            <th>PRICE</th>
            <th>QUANTITY</th>
            <th>ACTION</th>
        </tr>

        <tr>
            <?php

            $selectQuery = mysqli_query($connect, "select * from product");
            $QuantityZero = false;
            if (mysqli_num_rows($selectQuery)) {
                while ($row = mysqli_fetch_assoc($selectQuery)) { ?>

                <td><?php echo $row['id']; ?></td>
                <td><?php echo $row['name']; ?></td>
                <td><?php echo $row['price']."$"; ?></td>
                <td><?php if ($row['quantity']<1) {echo "The product not available";}else{echo $row['quantity'];}?></td>
                        <td><a href="buy.php?id=<?php echo $row['id'];  ?>">Buy</a> &nbsp; <a style="color: green;" href="add.php?id=<?php echo $row['id']; ?>">Add</a></td>

                </tr>
                <?php
                }
            }

            ?>



    </table>

</body>

</html>
Original buy.php (with bugs: for study only)⁦buy.php⁩ـی ڕەسەن (بە باگەوە: تەنها بۆ خوێندن)buy.phpPHP
<!DOCTYPE html>
<html lang="en">



<?php

$connect = mysqli_connect('localhost', 'root', '', 'MyStudy');

if (!$connect) {
    die(mysqli_error($connect));
}

?>

<body>
    <?php

        $id = $_GET['id'];
        $updateQuery = mysqli_query($connect, "update product set quantity = (quantity - 1) where id = '$id' and quantity > 0");
        if ($updateQuery) {
            header('location: product.php');
        } else {
            die(mysqli_error($connect));
        }



    ?>
</body>

</html>
Original add.php (with bugs: for study only)⁦add.php⁩ـی ڕەسەن (بە باگەوە: تەنها بۆ خوێندن)add.phpPHP
<!DOCTYPE html>
<html lang="en">



<?php

$connect = mysqli_connect('localhost', 'root', '', 'MyStudy');

if (!$connect) {
    die(mysqli_error($connect));
}

?>

<body>
    <?php

        $id = $_GET['id'];
        $updateQuery = mysqli_query($connect, "update product set quantity = (quantity + 1) where id = '$id'");
        if ($updateQuery) {
            header('location: product.php');
        } else {
            die(mysqli_error($connect));
        }



    ?>
</body>

</html>
Hintئاماژە

Look at where <tr> opens, at what comes before header(), at the SQL, and at what a plain link can do.

سەیری ئەوە بکە <tr> لە کوێ دەکرێتەوە، چی پێش header() دێت، SQLـەکە و بەستەرێکی ئاسایی دەتوانێت چی بکات.

Show answerوەڵامەکە پیشان بدە
  1. <tr> outside the loop (product.php): it opens once before the loop but closes inside it, so every row after the first has no opening <tr>. The output below shows it.
  2. SQL injection (buy.php, add.php): where id = '$id' with $id = $_GET['id']. Validate the id and use a prepared statement.
  3. Data changed by plain links (buy.php?id=…, add.php?id=…): browsers, link previews and other sites can open them. Use POST forms.
  4. header() after HTML (buy.php, add.php): <!DOCTYPE html><html lang="en"> is sent first, so with output buffering off PHP says "Cannot modify header information - headers already sent". It works on XAMPP only because XAMPP's php.ini buffers output. Put all PHP before any HTML, and add exit; after header().
  5. No feedback: when the stock is already 0, the UPDATE "succeeds" with 0 rows changed and the user is told nothing. Check affected_rows.
  6. Unescaped output of name and price, and die(mysqli_error($connect)) shows database details (and cannot even report a failed connection, because $connect is then false).
  7. Small things: $QuantityZero is never used; "The product not available" is missing "is"; the price is printed as 10$ instead of $10; the connection code is copied into every file instead of one config.php; add.php has no upper limit.
  1. <tr> لە دەرەوەی خولەکە ⁦(product.php)⁩: یەک جار پێش خولەکە دەکرێتەوە بەڵام لەناویدا دادەخرێت، بۆیە هەموو ڕیزێک دوای یەکەم <tr>ـی کردنەوەی نییە. دەرخستەی خوارەوە پیشانی دەدات.
  2. (⁦buy.php⁩، ⁦add.php⁩): where id = '$id' لەگەڵ $id = $_GET['id']. id پشتڕاست بکەرەوە و بەکاربهێنە.
  3. دراوە بە بەستەری ئاسایی دەگۆڕدرێت (buy.php?id=…، add.php?id=…): تۆڕگەڕ، پێشبینینی بەستەر و تۆڕگەکانی تر دەتوانن بیانکەنەوە. فۆرمی POST بەکاربهێنە.
  4. header() دوای HTML (⁦buy.php⁩، ⁦add.php⁩): سەرەتا <!DOCTYPE html><html lang="en"> دەنێردرێت، بۆیە کاتێک ⁦output buffering⁩ کوژاوەتەوە PHP دەڵێت «⁦Cannot modify header information - headers already sent⁩». لە XAMPPـدا تەنها کار دەکات چونکە ⁦php.ini⁩ـی XAMPP دەرخستە کۆدەکاتەوە. هەموو PHP پێش هەر HTMLێک دابنێ و دوای header() exit; زیاد بکە.
  5. هیچ وەڵامێک نییە: کاتێک کۆگا پێشتر 0ـە، UPDATEـەکە «سەردەکەوێت» بە 0 ڕیزی گۆڕاو و هیچ شتێک بە بەکارهێنەر ناوترێت. affected_rows بپشکنە.
  6. دەرخستەی بێزیان‌نەکراوی ناو و نرخ و die(mysqli_error($connect)) وردەکاری بنکەدراوە پیشان دەدات (و تەنانەت ناتوانێت پەیوەندییەکی شکستخواردوو ڕاپۆرت بکات، چونکە ئەوکات $connect دەبێت بە false).
  7. شتی بچووک: $QuantityZero هەرگیز بەکارنایەت؛ «⁦The product not available⁩» وشەی «is»ـی کەمە؛ نرخ وەک 10$ چاپ دەکرێت لە جیاتی $10؛ کۆدی پەیوەندییەکە لە هەموو فایلێکدا کۆپی کراوە لە جیاتی یەک config.php؛ ⁦add.php⁩ هیچ سنوورێکی سەرەوەی نییە.
The <tr> bug in miniatureباگی <tr> بە شێوەیەکی بچووکtr-demo.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
$rows = [['id' => 1, 'name' => 'Notebook'], ['id' => 2, 'name' => 'Pen']];
?>
<table border="1">
    <tr>
        <?php foreach ($rows as $row) { ?>
        <td><?php echo $row['id']; ?></td>
        <td><?php echo $row['name']; ?></td>
    </tr>
        <?php } ?>
</table>
Fixed product.php⁦product.php⁩ـی چاککراوproduct.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
require "config.php";            // gives $conn and e()

$messages = ['bought' => 'Thank you! One item was sold.', 'soldout' => 'Sorry, this product is sold out.',
             'added' => 'One item was added to the stock.'];
$status = $_GET['status'] ?? '';
$result = $conn->query("SELECT id, name, price, quantity FROM product ORDER BY id");
?>
<!DOCTYPE html>
<html lang="en">
<body>
  <?php if (isset($messages[$status])): ?>
    <p><?= e($messages[$status]) ?></p>
  <?php endif; ?>
  <table border="1">
    <tr>
      <th>ID</th><th>PRODUCT NAME</th><th>PRICE</th><th>QUANTITY</th><th>ACTION</th>
    </tr>
    <?php while ($row = $result->fetch_assoc()): ?>
    <tr>
      <td><?= e($row['id']) ?></td>
      <td><?= e($row['name']) ?></td>
      <td>$<?= e($row['price']) ?></td>
      <td><?= $row['quantity'] < 1 ? "Not available" : e($row['quantity']) ?></td>
      <td>
        <form action="buy.php" method="post" style="display:inline">
          <input type="hidden" name="id" value="<?= e($row['id']) ?>">
          <button type="submit">Buy</button>
        </form>
        <form action="add.php" method="post" style="display:inline">
          <input type="hidden" name="id" value="<?= e($row['id']) ?>">
          <button type="submit" style="color: green;">Add</button>
        </form>
      </td>
    </tr>
    <?php endwhile; ?>
  </table>
</body>
</html>
Fixed buy.php (add.php is the same with quantity + 1, no AND quantity > 0, and the status "added")⁦buy.php⁩ـی چاککراو (⁦add.php⁩ هەمان شتە بە quantity + 1، بێ AND quantity > 0 و دۆخی «added»)buy.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
require "config.php";            // all PHP work happens BEFORE any HTML is sent

$status = "soldout";
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $id = filter_var($_POST['id'] ?? null, FILTER_VALIDATE_INT);
    if ($id !== false && $id !== null) {
        // "AND quantity > 0" keeps the stock from going below zero
        $stmt = $conn->prepare("UPDATE product SET quantity = quantity - 1 WHERE id = ? AND quantity > 0");
        $stmt->bind_param("i", $id);
        $stmt->execute();
        if ($stmt->affected_rows === 1) {
            $status = "bought";
        }
    }
}
header("Location: product.php?status=" . $status);
exit;
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 4

Pessimistic default: if nothing is bought, the message says sold out.

: ئەگەر هیچ نەکڕدرێت، پەیامەکە دەڵێت کۆگا تەواو بووە.

Lineدێڕی 9

AND quantity > 0 keeps the stock from going below zero.

AND quantity > 0 ڕێگە دەگرێت کۆگا لە سفر کەمتر بێت.

Lineدێڕی 12

affected_rows is 1 only when a row really changed.

affected_rows تەنها کاتێک 1ـە کە ڕیزێک بە ڕاستی گۆڕابێت.

Lineدێڕی 17-18

Redirect and stop; nothing was printed before this line.

ئاڕاستە بکەرەوە و بوەستە؛ پێش ئەم دێڕە هیچ چاپ نەکراوە.

Find the bugs: students' CRUD projectsباگەکان بدۆزەرەوە: پڕۆژەکانی CRUDـی قوتابیان

B. The user-management pagesB. لاپەڕەکانی بەڕێوەبردنی بەکارهێنەران

ExerciseڕاهێنانB. Find the bugs: the user-management pagesB. باگەکان بدۆزەرەوە: لاپەڕەکانی بەڕێوەبردنی بەکارهێنەران

The same students wrote a small user-management area: View.php (only for logged-in users) lists the users with their pictures, update.php changes a username and password, and delete.php removes a user. Passwords are stored with md5(). Find every bug and security problem, then fix the three pages.

هەمان قوتابییان بەشێکی بچووکی بەڕێوەبردنی بەکارهێنەرانیان نووسی: View.php (تەنها بۆ بەکارهێنەرانی چووەژوورەوە) بەکارهێنەران لەگەڵ وێنەکانیان پیشان دەدات، update.php و تێپەڕەوشە دەگۆڕێت و delete.php بەکارهێنەرێک لادەبات. تێپەڕەوشە بە md5() هەڵدەگیرێت. هەموو باگێک و کێشەیەکی ئاسایشی بدۆزەرەوە، پاشان سێ لاپەڕەکە چاک بکە.

Original update.php (with bugs: for study only)⁦update.php⁩ـی ڕەسەن (بە باگەوە: تەنها بۆ خوێندن)update.phpPHP
<!DOCTYPE html>
<html lang="en">

<body>



<?php

include('config.php');
$id = $_GET['id'];

$selectQuery = mysqli_query($connection,"select * from users where id = '$id'");

if (mysqli_num_rows($selectQuery)){
    while($row = mysqli_fetch_assoc($selectQuery)){

        $username = $row['username'];
        $password = $row['password'];
    }

}


if (isset($_POST['Update'])){
    $username = $_POST['user'];
    $password = md5($_POST['pass']);

    $UpdateQuery = mysqli_query($connection,"Update users set username = '$username', password='$password' where id ='$id'");

    if ($UpdateQuery){
        header('location: View.php');
    }
    else{
        die(mysqli_error($connection));
    }
}


?>
    <form method="post">

    username : <input type="text" name="user" value="<?php echo $username ?> "><br><br>
    password : <input type="password" name="pass" value="<?php echo $password ?>"><br><br>
    <input type="submit" name="Update" value="Update">
    </form>
</body>
</html>
Original View.php (the screenshot is cut at the end) (with bugs: for study only)⁦View.php⁩ـی ڕەسەن (وێنەکە لە کۆتاییدا بڕاوە) (بە باگەوە: تەنها بۆ خوێندن)View.phpPHP
<!DOCTYPE html>
<html lang="en">

<body>
    <?php

    include('config.php');

    session_start();

    if (!isset($_SESSION['userId'])){
        header('Location: Register.php');

    }

    if (!isset($_SESSION['intCount'])){

        $_SESSION['intCount']=1;
    }
    else{
        $_SESSION['intCount']++;
    }

    echo "<h2> Welcome ".$_SESSION['userInput']."</h2>";
    echo "<h2> Counter ".$_SESSION['intCount']."....</h2>";


     echo "<a href='logout.php'>Logout</a>";





    ?>

    <table border="10" align="center">
        <tr>
            <th>ID</th>
            <th>Usrename</th>
            <th>Passowrd</th>
            <th>Image</th>
            <th>Action</th>
        </tr>

        <tr>
            <?php
            $selectionQuery = mysqli_query($connection, "select * from users");

            if (mysqli_num_rows($selectionQuery)) {
                while ($row = mysqli_fetch_assoc($selectionQuery)) { ?>

                    <td>
                        <?php echo $row['id']; ?>
                    </td>
                    <td>
                        <?php echo $row['username']; ?>
                    </td>
                    <td>
                        <?php echo $row['password']; ?>
                    </td>
                    <td> <img width="70px" height="70px" src="uploads/<?php echo $row['filename']; ?>"> </td>
                    <td><a href="update.php?id=<?php echo $row['id']; ?>">Edit</a> &nbsp; <a
                            href="delete.php?id=<?php echo $row['id']; ?>">delete</a></td>
        <!-- … the screenshot ends here … -->
Original delete.php (with bugs: for study only)⁦delete.php⁩ـی ڕەسەن (بە باگەوە: تەنها بۆ خوێندن)delete.phpPHP
<?php
include('config.php');


$id = $_GET['id'];

$deleteQuery = mysqli_query($connection,"delete from users where id='$id'");

if ($deleteQuery){
    header('Location: View.php');
}
else{
    die(mysqli_error($connection));
}


?>
Hintئاماژە

Ask for each page: what happens to a visitor who is not logged in? What does the password box contain? What happens if the user clicks Update without typing?

بۆ هەر لاپەڕەیەک بپرسە: چی بەسەر سەردانیکەرێک دێت کە نەچووەتە ژوورەوە؟ چی تێدایە؟ چی ڕوودەدات ئەگەر بەکارهێنەر بێ نووسین کرتە لە Update بکات؟

Show answerوەڵامەکە پیشان بدە
  1. Missing exit after the redirect (View.php): header('Location: Register.php') only adds a header, so the script keeps running and sends the whole users table, passwords included, to a visitor who is not logged in. This is the most serious bug.
  2. session_start() and header() after HTML (View.php, update.php): <!DOCTYPE html> is sent first. With output buffering off, PHP warns "Session cannot be started after headers have already been sent". Start the session on the very first line.
  3. Double hashing (update.php): the password box is pre-filled with the stored md5 hash. If the user clicks Update without typing, md5() of the hash is saved and the old password stops working (shown below).
  4. A trailing space (update.php): value="<?php echo $username ?> " adds a space after the name, so every update saves "hana " instead of "hana" (shown below). Whether a later search for 'hana' still finds "hana " depends on the column's : the usual MariaDB collations ignore spaces at the end, but MySQL 8's default utf8mb4_0900_ai_ci does not.
  5. SQL injection in all three pages ('$id', '$username'); with delete.php?id=1' OR '1'='1 the DELETE removes every user.
  6. No login check in update.php and delete.php: anyone who knows the URL can edit or delete users. Deleting by a GET link is also unsafe.
  7. md5 passwords, and View.php shows the password hashes in a table column.
  8. Unescaped output of usernames, the welcome name and the file name; the deleted user's picture stays in uploads/; no exit after the other redirects.
  9. Small things: "Usrename", "Passowrd"; align="center" is obsolete HTML; width="70px" should be width="70"; the same <tr> outside the loop as in exercise A; the redirect target is View.php here but view.php in the login page, which fails on a case-sensitive Linux server.
  1. exit دوای ئاڕاستەکردنەوە نییە ⁦(View.php)⁩: header('Location: Register.php') تەنها سەردێڕێک زیاد دەکات، بۆیە دەسنووسەکە بەردەوام دەبێت و هەموو خشتەی بەکارهێنەران، لەگەڵ تێپەڕەوشەکان، دەنێرێت بۆ سەردانیکەرێک کە نەچووەتە ژوورەوە. ئەمە مەترسیدارترین باگە.
  2. session_start() و header() دوای HTML (⁦View.php⁩، ⁦update.php⁩): سەرەتا <!DOCTYPE html> دەنێردرێت. کاتێک ⁦output buffering⁩ کوژاوەتەوە، PHP ئاگادار دەکاتەوە «⁦Session cannot be started after headers have already been sent⁩». سێشن لە یەکەم دێڕدا دەست پێبکە.
  3. دوو جار هاشکردن ⁦(update.php)⁩: بە هاشی ⁦md5⁩ـی هەڵگیراو پێشوەختە پڕ دەکرێتەوە. ئەگەر بەکارهێنەر بێ نووسین کرتە لە Update بکات، md5()ـی هاشەکە هەڵدەگیرێت و تێپەڕەوشە کۆنەکە ئیتر کار ناکات (لە خوارەوە پیشان دراوە).
  4. بۆشاییەکی کۆتایی ⁦(update.php)⁩: value="<?php echo $username ?> " بۆشاییەک دوای ناوەکە زیاد دەکات، بۆیە هەموو نوێکردنەوەیەک «hana » هەڵدەگرێت لە جیاتی «hana» (لە خوارەوە پیشان دراوە). ئایا گەڕانێکی دواتر بۆ 'hana' هێشتا "hana " دەدۆزێتەوە یان نا، بەندە بە ستوونەکە: لە MariaDBـدا ئاسایی گوێ بە بۆشایی کۆتایی نادات، بەڵام utf8mb4_0900_ai_ci، کە بنەڕەتیی ⁦MySQL 8⁩ـە، گوێی پێدەدات.
  5. لە هەر سێ لاپەڕەکەدا ('$id'، '$username')؛ بە delete.php?id=1' OR '1'='1 هەموو بەکارهێنەرێک لادەبات.
  6. پشکنینی چوونەژوورەوە نییە لە ⁦update.php⁩ و ⁦delete.php⁩: هەر کەسێک URLـەکە بزانێت دەتوانێت بەکارهێنەران دەستکاری بکات یان بسڕێتەوە. سڕینەوە بە بەستەری GETـیش نەپارێزراوە.
  7. تێپەڕەوشەی ⁦md5⁩ و ⁦View.php⁩ هاشی تێپەڕەوشەکان لە ستوونێکی خشتەدا پیشان دەدات.
  8. دەرخستەی بێزیان‌نەکراوی ناوی بەکارهێنەران، ناوی بەخێرهاتن و ناوی فایل؛ وێنەی بەکارهێنەری سڕاوە لە uploads/دا دەمێنێتەوە؛ دوای ئاڕاستەکردنەوەکانی تر exit نییە.
  9. شتی بچووک: «Usrename»، «Passowrd»؛ align="center" HTMLـی کۆنە؛ width="70px" دەبێت width="70" بێت؛ هەمان <tr>ـی دەرەوەی خول وەک ڕاهێنانی A؛ ئامانجی ئاڕاستەکردنەوە لێرە View.phpـە بەڵام لە لاپەڕەی چوونەژوورەوەدا view.php، کە لەسەر ڕاژەکارێکی Linux کە شکست دەهێنێت.
Why a pre-filled password box breaks the accountبۆچی خانەی تێپەڕەوشەی پڕکراوە هەژمارەکە تێک دەداتdouble-hash-demo.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
$stored = md5("hana2026");                 // what the database holds
echo "Stored:          ", $stored, "\n";
// update.php pre-fills the password box with $stored; the user clicks Update without typing
$saved_now = md5($stored);                 // update.php runs md5() on the hash
echo "Saved after edit: ", $saved_now, "\n";
var_dump(md5("hana2026") === $saved_now);  // can the user still log in with hana2026?
The space before the closing quoteبۆشاییەکە پێش کۆتەیشنی کۆتاییtrailing-space-demo.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
$username = "hana";
?>
<input type="text" name="user" value="<?php echo $username ?> ">
Fixed View.php⁦View.php⁩ـی چاککراوView.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
session_start();                          // the very first thing: before any HTML is sent
if (!isset($_SESSION['userId'])) {
    header('Location: login.php');
    exit;                                 // without exit, the whole page would still be sent!
}
require "config.php";

$_SESSION['intCount'] = ($_SESSION['intCount'] ?? 0) + 1;
$result = $conn->query("SELECT id, username, filename FROM users ORDER BY id");   // no passwords
?>
<!DOCTYPE html>
<html lang="en">
<body>
  <h2>Welcome <?= e($_SESSION['userInput']) ?></h2>
  <h2>Counter <?= e($_SESSION['intCount']) ?></h2>
  <a href="logout.php">Logout</a>
  <table border="1">
    <tr><th>ID</th><th>Username</th><th>Image</th><th>Action</th></tr>
    <?php while ($row = $result->fetch_assoc()): ?>
    <tr>
      <td><?= e($row['id']) ?></td>
      <td><?= e($row['username']) ?></td>
      <td><img width="70" height="70" src="uploads/<?= e(rawurlencode($row['filename'])) ?>" alt=""></td>
      <td>
        <a href="update.php?id=<?= e($row['id']) ?>">Edit</a>
        <form action="delete.php" method="post" style="display:inline"
              onsubmit="return confirm('Delete this user?');">
          <input type="hidden" name="id" value="<?= e($row['id']) ?>">
          <button type="submit">Delete</button>
        </form>
      </td>
    </tr>
    <?php endwhile; ?>
  </table>
</body>
</html>
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 2

Start the session before any HTML.

سێشن پێش هەر HTMLێک دەست پێبکە.

Lineدێڕی 4-5

Not logged in: go to the login page and stop.

نەچووەتە ژوورەوە: بڕۆ بۆ لاپەڕەی چوونەژوورەوە و بوەستە.

Lineدێڕی 9

The visit counter, written in one line with ??.

ژمێرەری سەردان، بە یەک دێڕ بە ?? نووسراوە.

Lineدێڕی 10

Select only what the page shows: no passwords.

تەنها ئەوە هەڵبژێرە کە لاپەڕەکە پیشانی دەدات: بێ تێپەڕەوشە.

Fixed update.php⁦update.php⁩ـی چاککراوupdate.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
session_start();
if (!isset($_SESSION['userId'])) {
    header('Location: login.php');
    exit;
}
require "config.php";

$id = filter_var($_GET['id'] ?? null, FILTER_VALIDATE_INT);
if ($id === false || $id === null) {
    header('Location: View.php');
    exit;
}

if (isset($_POST['Update'])) {
    $username = trim($_POST['user'] ?? '');
    $newPass  = $_POST['pass'] ?? '';
    if ($username !== '') {
        $stmt = $conn->prepare("UPDATE users SET username = ? WHERE id = ?");
        $stmt->bind_param("si", $username, $id);
        $stmt->execute();
        if ($newPass !== '') {                            // only when a NEW password was typed
            $hash = password_hash($newPass, PASSWORD_DEFAULT);
            $stmt = $conn->prepare("UPDATE users SET password = ? WHERE id = ?");
            $stmt->bind_param("si", $hash, $id);
            $stmt->execute();
        }
        header('Location: View.php');
        exit;
    }
}

$stmt = $conn->prepare("SELECT username FROM users WHERE id = ?");
$stmt->bind_param("i", $id);
$stmt->execute();
$user = $stmt->get_result()->fetch_assoc();
if ($user === null) {
    header('Location: View.php');
    exit;
}
?>
<!DOCTYPE html>
<html lang="en">
<body>
  <form method="post">
    username : <input type="text" name="user" value="<?= e($user['username']) ?>"><br><br>
    new password (leave empty to keep it) : <input type="password" name="pass"><br><br>
    <input type="submit" name="Update" value="Update">
  </form>
</body>
</html>
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 9-13

Validate the id from the URL before anything else.

پێش هەموو شتێک idـی URLـەکە پشتڕاست بکەرەوە.

Lineدێڕی 16

trim() removes accidental spaces.

trim() بۆشایی بە هەڵە لادەبات.

Lineدێڕی 22-27

Change the password only when a new one is typed, and use password_hash().

تەنها کاتێک تێپەڕەوشە بگۆڕە کە نوێیەک نووسرابێت و password_hash() بەکاربهێنە.

Fixed delete.php⁦delete.php⁩ـی چاککراوdelete.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
session_start();
if (!isset($_SESSION['userId'])) {
    header('Location: login.php');
    exit;
}
require "config.php";

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $id = filter_var($_POST['id'] ?? null, FILTER_VALIDATE_INT);
    if ($id !== false && $id !== null) {
        // find the user's picture first, so we can remove the file too
        $stmt = $conn->prepare("SELECT filename FROM users WHERE id = ?");
        $stmt->bind_param("i", $id);
        $stmt->execute();
        $user = $stmt->get_result()->fetch_assoc();

        if ($user !== null) {
            $stmt = $conn->prepare("DELETE FROM users WHERE id = ?");
            $stmt->bind_param("i", $id);
            $stmt->execute();

            $file = "uploads/" . basename($user['filename']);
            if (is_file($file)) {
                unlink($file);
            }
        }
    }
}
header('Location: View.php');
exit;
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 13-16

Read the picture's file name first.

سەرەتا ناوی فایلی وێنەکە بخوێنەوە.

Lineدێڕی 23-26

basename() keeps the path inside uploads/; then delete the file.

basename() ڕێڕەوەکە لەناو uploads/دا دەهێڵێتەوە؛ پاشان فایلەکە بسڕەوە.

A complete secure login (with password_verify()) for this project is built in A login system. Note that here any logged-in user may still edit every user; a real admin area would also check the user's role.

چوونەژوورەوەیەکی تەواو و سەلامەت (بە password_verify()) بۆ ئەم پڕۆژەیە لە سیستەمی چوونەژوورەوە دروست دەکرێت. ئاگاداربە کە لێرەدا هەر بەکارهێنەرێکی چووەژوورەوە هێشتا دەتوانێت هەموو بەکارهێنەرێک دەستکاری بکات؛ بەشێکی ڕاستەقینەی بەڕێوەبەر ڕۆڵی بەکارهێنەریش دەپشکنێت.

Find the bugs: students' CRUD projectsباگەکان بدۆزەرەوە: پڕۆژەکانی CRUDـی قوتابیان

C. The diagram's update and delete⁦C. update⁩ و deleteـی ناو وێنەکە

ExerciseڕاهێنانC. Find the bugs: the diagram's update and deleteC. باگەکان بدۆزەرەوە: update و deleteـی ناو وێنەکە

These are the update.php and delete.php boxes from the student's page-map diagram. List what is wrong with them.

ئەمانە سندوقەکانی update.php و delete.phpـن لە وێنەی نەخشەی لاپەڕەکانی قوتابییەکەدا. بنووسە چییان تێدا هەڵەیە.

From the diagram: update.php (with bugs: for study only)لە وێنەکەوە: ⁦update.php⁩ (بە باگەوە: تەنها بۆ خوێندن)update.phpPHP
<html>
<body>
<?php
include "Config.php";

if (isset($_GET['id'])) {
$user_id = $_GET['id'];
$sql = "select * from student where id='$user_id'";
$result = mysqli_query($db_connect, $sql);

while ($row = mysqli_fetch_assoc($result)) {
$username = $row['username']; //Used in the HTML (usefull for sticky form)
$email = $row['email'];
$password = $row['password'];
$gender = $row['gender'];

$id = $row['id'];
}
}

if (isset($_POST['update'])) {
$usernamee = $_POST['username'];
$user_id = $_POST['id'];
$email = $_POST['email'];
$gender = $_POST['gender'];
$password = $_POST['password'];

$sql = "update student set username='$usernamee',email='$email', password='$password',gender='$gender' where id='$user_id'";
$result = mysqli_query($db_connect, $sql);

if ($result) {
echo "Record updated successfully." . header('Location: view.php');
} else {
echo "Error:" . mysqli_error($db_connect);
}
}
?>


<h2>Update Form</h2>
<form method="post">
<fieldset>
<legend>Personal information:</legend>

First name:<br>
<input type="text" name="username" value="<?php echo $username; ?>">

<input type="hidden" name="id" value="<?php echo $id; ?>"><br />

Last name:<br />
<input type="text" name="email" value="<?php echo $email; ?>"><br />

Password:<br />
<input type="password" name="password" value="<?php echo $password; ?>"><br />

Gender:<br />
<input type="radio" name="gender" value="Male" <?php if ($gender == 'Male') echo "checked"; ?>>Male
<input type="radio" name="gender" value="Female" <?php if ($gender == 'Female') echo "checked"; ?>>Female
<br /><br />
<input type="submit" value="Update" name="update">
</fieldset>
</form>
</body>
</html>
From the diagram: delete.php (with bugs: for study only)لە وێنەکەوە: ⁦delete.php⁩ (بە باگەوە: تەنها بۆ خوێندن)delete.phpPHP
<?php
include("Config.php");

$user_id = $_GET['id'];
$sql = "delete from student where id = $user_id";
$result = mysqli_query($db_connect, $sql);

if ($result) {
echo "sucess" . header('Location: view.php');
} else {
die(mysqli_error($db_connect));
}
?>
Show answerوەڵامەکە پیشان بدە
  1. SQL injection in all three queries. In delete.php the id is not even in quotes, so 1 OR 1=1 deletes every student.
  2. header() after HTML output in update.php: <html><body> is printed first, so the redirect fails with "headers already sent" (unless output buffering hides it). echo "Record updated successfully." . header(...) is also strange: header() returns nothing, and the message would never be seen after a redirect anyway. Redirect first, then exit.
  3. Password in plain text, stored and printed into the password box.
  4. Wrong labels: the field labelled "Last name" is really the email field; $usernamee is a typo (it works only because it is used consistently).
  5. Undefined variables when the page is opened without ?id=, and no escaping of the printed values (XSS).
  6. delete.php: no isset() check, deletion by GET, "sucess" is misspelt, and die(mysqli_error(...)) shows database details.
  1. لە هەر سێ پرسەکەدا. لە ⁦delete.php⁩ـدا id تەنانەت لەناو کۆتەیشنیشدا نییە، بۆیە 1 OR 1=1 هەموو قوتابییەک دەسڕێتەوە.
  2. header() دوای دەرخستەی HTML لە ⁦update.php⁩: سەرەتا <html><body> چاپ دەکرێت، بۆیە ئاڕاستەکردنەوەکە بە «⁦headers already sent⁩» شکست دەهێنێت (مەگەر ⁦output buffering⁩ بیشارێتەوە). echo "Record updated successfully." . header(...)ـیش سەیرە: header() هیچ ناگەڕێنێتەوە و پەیامەکە دوای ئاڕاستەکردنەوە بە هەر حاڵ نابینرێت. سەرەتا ئاڕاستە بکەرەوە، پاشان exit.
  3. تێپەڕەوشە وەک ، هەڵدەگیرێت و لەناو خانەی تێپەڕەوشەدا چاپ دەکرێت.
  4. ناونیشانی هەڵە: ئەو خانەیەی ناونیشانی «⁦Last name⁩»ـی هەیە لە ڕاستیدا خانەی ئیمەیڵە؛ $usernamee هەڵەی نووسینە (تەنها کار دەکات چونکە هەمیشە بە هەمان شێوە بەکارهاتووە).
  5. گۆڕاوی پێناسەنەکراو کاتێک لاپەڕەکە بەبێ ?id= دەکرێتەوە و هیچ بێزیانکردنێک بۆ نرخە چاپکراوەکان نییە ⁦(XSS)⁩.
  6. ⁦delete.php⁩: پشکنینی isset() نییە، سڕینەوە بە GET، «sucess» هەڵە نووسراوە و die(mysqli_error(...)) وردەکاری بنکەدراوە پیشان دەدات.

The secure update.php and delete.php of this lecture fix all of these.

⁦update.php⁩ و ⁦delete.php⁩ـی سەلامەتی ئەم وانەیە هەموو ئەمانە چاک دەکەن.

Lab tasksئەرکەکانی تاقیگە

These are the tasks of Lecture 8 and Lab 8. Together they build the whole application. The answers point to the secure code in this lecture.

ئەمانە ئەرکەکانی وانەی ٨ و تاقیگەی ٨ن. پێکەوە هەموو بەرنامەکە دروست دەکەن. وەڵامەکان ئاماژە بە کۆدە سەلامەتەکەی ئەم وانەیە دەکەن.

Exerciseڕاهێنان1. Connect: config.php١. پەیوەندی: ⁦config.php⁩

Establish a database connection with your PHP code. The code works as a link between the web page and the database. Name the file config.php.

بە کۆدی PHPـەکەت دروست بکە. کۆدەکە وەک پردێک لە نێوان لاپەڕەکە و بنکەدراوەکەدا کار دەکات. ناوی فایلەکە بنێ config.php.

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

See the improved config.php: mysqli_report(), new mysqli() inside try, set_charset("utf8mb4"), a short message on failure, and the e() helper.

⁦config.php⁩ـی باشترکراو ببینە: mysqli_report()، new mysqli() لەناو try، set_charset("utf8mb4")، پەیامێکی کورت لە کاتی شکستدا و کردارە یاریدەدەرەکەی e().

Exerciseڕاهێنان2. Create: create.php٢. دروستکردن: ⁦create.php⁩

Create: write the page that inserts a new user from the Signup form (first name, last name, email, password, gender). Name it create.php.

Create: ئەو لاپەڕەیە بنووسە کە بەکارهێنەرێکی نوێ لە فۆرمی خۆتۆمارکردنەوە زیاد دەکات (ناوی یەکەم، ناوی کۆتایی، ئیمەیڵ، تێپەڕەوشە، ڕەگەز). ناوی بنێ create.php.

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

See the secure create.php: server-side validation, a duplicate-email check, password_hash(), a prepared INSERT, and a redirect to view.php.

⁦create.php⁩ـی سەلامەت ببینە: ، پشکنینی ئیمەیڵی دووبارە، password_hash()، INSERTێکی ئامادەکراو و ئاڕاستەکردنەوە بۆ view.php.

Exerciseڕاهێنان3. Read: view.php٣. خوێندنەوە: ⁦view.php⁩

Read: create a page that displays the records from the table users. Name it view.php.

Read: لاپەڕەیەک دروست بکە کە تۆمارەکانی خشتەی users پیشان بدات. ناوی بنێ view.php.

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

See the secure view.php: a SELECT of the needed columns, every value printed with e(), an Edit link and a Delete POST form for every row.

⁦view.php⁩ـی سەلامەت ببینە: SELECTێک بۆ ستوونە پێویستەکان، هەموو نرخێک بە e() چاپ دەکرێت، بەستەری Edit و فۆرمی POSTـی Delete بۆ هەموو ڕیزێک.

Exerciseڕاهێنان4. Update: update.php٤. نوێکردنەوە: ⁦update.php⁩

Update: create another page to update the details of a user in the database. Name it update.php.

Update: لاپەڕەیەکی تر دروست بکە بۆ نوێکردنەوەی وردەکارییەکانی بەکارهێنەرێک لە بنکەدراوەکەدا. ناوی بنێ update.php.

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

See the secure update.php: GET with ?id= loads the user with a prepared SELECT; the POST validates, runs a prepared UPDATE, changes the password only when a new one is typed, and redirects.

⁦update.php⁩ـی سەلامەت ببینە: GET بە ?id= بەکارهێنەرەکە بە SELECTێکی ئامادەکراو دەهێنێت؛ POSTـەکە پشتڕاست دەکاتەوە، UPDATEێکی ئامادەکراو جێبەجێ دەکات، تەنها کاتێک تێپەڕەوشە دەگۆڕێت کە نوێیەک نووسرابێت و ئاڕاستە دەکاتەوە.

Exerciseڕاهێنان5. Delete: delete.php٥. سڕینەوە: ⁦delete.php⁩

Delete (the in-class task): create a page that lets the user choose a record and delete it from the database. Name it delete.php.

Delete (ئەرکی ناو پۆل): لاپەڕەیەک دروست بکە کە ڕێگە بە بەکارهێنەر بدات تۆمارێک هەڵبژێرێت و لە بنکەدراوەکەدا بیسڕێتەوە. ناوی بنێ delete.php.

Hintئاماژە

The user chooses the record with the Delete button in view.php.

بەکارهێنەر تۆمارەکە بە دوگمەی Delete لە view.phpـدا هەڵدەبژێرێت.

Show answerوەڵامەکە پیشان بدە
delete.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
require "config.php";

// Deleting changes data, so accept it only from a POST form - never from a plain link
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $user_id = filter_var($_POST['id'] ?? null, FILTER_VALIDATE_INT);
    if ($user_id !== false && $user_id !== null) {
        $stmt = $conn->prepare("DELETE FROM users WHERE id = ?");
        $stmt->bind_param("i", $user_id);
        $stmt->execute();
    }
}
header("Location: view.php");
exit;

The Delete button in view.php is a small POST form with a hidden id and a confirm() question. Lecture 8's own answer is shown, with its problems, in the Delete section.

دوگمەی Delete لە view.phpـدا فۆرمێکی بچووکی POSTـە بە idـێکی شاراوە و پرسیارێکی confirm(). وەڵامی خودی وانەی ٨، لەگەڵ کێشەکانی، لە بەشی Deleteدا پیشان دراوە.

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

The five files of the CRUD application.پێنج فایلەکەی بەرنامە CRUDـەکە.
PageلاپەڕەRequestداواکاریSQLKey safety pointsخاڵە سەرەکییەکانی سەلامەتی
config.phpincluded by allلە هەمووان هاوردە دەکرێت—try/catch, utf8mb4, own DB user, e()⁦try/catch⁩، ⁦utf8mb4⁩، بەکارهێنەری تایبەتی بنکەدراوە، e()
create.phpGET form, POST saveفۆرمی GET، پاشەکەوتی POSTINSERTvalidate, password_hash, prepared, redirect + exitپشتڕاستکردنەوە، ⁦password_hash⁩، ئامادەکراو، ئاڕاستەکردنەوە + exit
view.phpGETSELECTnamed columns, escape every value, Delete as POSTستوونی ناودار، بێزیانکردنی هەموو نرخێک، Delete بە POST
update.phpGET ?id=, POST save⁦GET ?id⁩=، پاشەکەوتی POSTUPDATEvalidate the hidden id, never pre-fill passwordsidـی شاراوە پشتڕاست بکەرەوە، هەرگیز تێپەڕەوشە پێشوەختە پڕ مەکەرەوە
delete.phpPOST onlyتەنها POSTDELETEvalidate id, prepared, confirm, (CSRF token)پشتڕاستکردنەوەی id، ئامادەکراو، confirm، (نیشانەی CSRF)
Review questionsپرسیارەکانی پێداچوونەوە
What does CRUD stand for, and which SQL command belongs to each letter?CRUD کورتکراوەی چییە و کام فەرمانی SQL سەر بە هەر پیتێکە؟

Create (INSERT), Read (SELECT), Update (UPDATE), Delete (DELETE): the four functions needed for a persistent storage application.

⁦Create (INSERT)⁩، ⁦Read (SELECT)⁩، ⁦Update (UPDATE)⁩، ⁦Delete (DELETE)⁩: ئەو چوار کارەی بۆ بەرنامەیەکی پێویستن.

What is a persistent storage application?بەرنامەی خەزنکردنی هەمیشەیی چییە؟

One that keeps data after the program or computer stops, unlike RAM or a cache. Examples: databases, file systems, object storage.

بەرنامەیەکە کە دراوە دوای وەستانی بەرنامەکە یان کۆمپیوتەرەکە دەپارێزێت، بە پێچەوانەی RAM یان cache. نموونە: بنکەدراوە، ، هەڵگرتنی ئۆبجێکت.

Does using CRUD make an application secure?ئایا بەکارهێنانی CRUD بەرنامەیەک سەلامەت دەکات؟

No. CRUD only names the operations. Security comes from login checks, authorization checks (for example WHERE owner_id = ?), prepared statements, validation and escaping.

نەخێر. CRUD تەنها ناوی کارەکانە. ئاسایش لە پشکنینی چوونەژوورەوە، پشکنینی ڕێگەپێدان (بۆ نموونە WHERE owner_id = ?)، ، پشتڕاستکردنەوە و بێزیانکردنەوە دێت.

What do num_rows and fetch_assoc() do in view.php?num_rows و fetch_assoc() لە ⁦view.php⁩ـدا چی دەکەن؟

num_rows is the number of rows in the result; fetch_assoc() returns the next row as an associative array, or null when there are no more.

num_rows ژمارەی ڕیزەکانی ئەنجامەکەیە؛ fetch_assoc() ڕیزی دواتر وەک دەگەڕێنێتەوە، یان null کاتێک هیچی تر نەماوە.

Why is exit; written after header("Location: view.php")?بۆچی exit; دوای header("Location: view.php") دەنووسرێت؟

header() only adds a header; the script keeps running. Without exit, a protected page can still send its content to someone who is not logged in.

header() تەنها سەردێڕێک زیاد دەکات؛ دەسنووسەکە بەردەوام دەبێت. بەبێ exit، لاپەڕەیەکی پارێزراو هێشتا دەتوانێت ناوەڕۆکەکەی بنێرێت بۆ کەسێک کە نەچووەتە ژوورەوە.

What is Post/Redirect/Get?⁦Post/Redirect/Get⁩ چییە؟

After a POST that changes data, redirect to a GET page. Refreshing then repeats only the harmless GET and does not insert the data twice.

دوای POSTێک کە دراوە دەگۆڕێت، ئاڕاستە بکەرەوە بۆ لاپەڕەیەکی GET. ئەوکات نوێکردنەوەی لاپەڕە تەنها GETـە بێزیانەکە دووبارە دەکاتەوە و دراوەکە دوو جار زیاد ناکات.

Why should the password box of an edit form be empty?بۆچی خانەی تێپەڕەوشەی فۆرمی دەستکاریکردن دەبێت بەتاڵ بێت؟

Pre-filling it shows the stored value in the page source, and saving it again can hash the hash so the password stops working. Change the password only when a new one is typed.

پڕکردنەوەی پێشوەختە نرخە هەڵگیراوەکە لە سەرچاوەی لاپەڕەکەدا پیشان دەدات و پاشەکەوتکردنەوەی دەتوانێت هاشەکە هاش بکاتەوە بە شێوەیەک کە تێپەڕەوشەکە ئیتر کار نەکات. تەنها کاتێک تێپەڕەوشە بگۆڕە کە نوێیەک نووسرابێت.

Videoڤیدیۆ

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

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

PHP For Absolute Beginners | 6.5 Hour CourseTraversy Media · Englishئینگلیزی · 711K viewsبینین · 16K likesلایک · Partبەشی 2:33:53–3:58:22 (ofلە 6:27:21)

Watch a full CRUD app being built with PHP and PDO: list products, add them, delete them with a POST form, and edit them.

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

Watch 2:33:53–3:58:22: a products CRUD with PDO, from listing to update.

لە ⁦2:33:53⁩ تا ⁦3:58:22⁩ سەیر بکە: بەرهەمەکان بە ، لە لیستکردنیانەوە تا دەستکاریکردنیان.

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

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

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

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

SQLite Basicsبنەماکانی SQLite

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

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