01

Project: PHP Syntax & Server-Side Basics

Vanilla PHP Course

Introduction

At this point you should know enough JavaScript to understand server-side concepts. We're going to bridge that knowledge to PHP by building a personal dashboard that demonstrates how PHP handles requests differently from Node.js and Express.

Goal: Understand PHP syntax differences from JavaScript and server-side rendering

Assignment

Set up a basic PHP development environment. Install PHP 8.0 or higher and verify with php -v. Create a project folder and start the built-in server with php -S localhost:8000.
Create an index.php file. Unlike Node.js where you need Express to handle routes, PHP files are accessed directly through URLs. Your index.php will be the homepage at http://localhost:8000.
Build a dashboard that displays the current server time in at least three different timezones. Use PHP's DateTime class and DateTimeZone. Remember, this runs on the server, not in the browser.
Add a section showing server information using the $_SERVER superglobal. Display PHP version with phpversion(), loaded extensions, and other server details. This is your first look at how PHP provides request data differently than Express's req object.
Create a dynamic greeting that changes based on the time of day (morning, afternoon, evening, night). Use conditional logic and PHP's date functions.
Build a calculator form with two number inputs and operation select (add, subtract, multiply, divide). When submitted, the form should POST to the same page. Handle the calculation using $_POST superglobal and display the result. Add server-side validation for empty fields and division by zero.
Add another form for unit conversion (celsius to fahrenheit, kilometers to miles, etc). This demonstrates handling multiple forms on one page, something you'd use separate routes for in Express.
Implement proper error handling. Display validation errors conditionally near the form fields. Use PHP's control structures to show/hide error messages.
Style your dashboard with basic CSS. You can include it inline or in a separate file. Notice how unlike React, you're mixing PHP and HTML in the same file initially.
PHP Syntax Superglobals Forms Server-Side

Key Differences from JavaScript/Node.js:

There's no event loop in PHP, so execution is synchronous. Each request is isolated rather than maintaining persistent connections. Variables use $ prefix. String concatenation uses . instead of +. The built-in server runs without needing an Express-like framework.

Project Submission

02

Project: Arrays, Loops & File I/O

Vanilla PHP Course

Introduction

Now that you understand basic PHP syntax, it's time to work with PHP's powerful array system and file operations. Unlike JavaScript's simple arrays and objects, PHP has both indexed and associative arrays with rich built-in functions.

Goal: Master PHP arrays (associative arrays) and basic file operations

Assignment

Create a reading list manager. Set up a data directory and a books.json file to store your data. This simulates a simple database before we move to MySQL.
Build a form to add books with fields for title, author, genre, rating (1-5 stars), and read status (checkbox). Each book should be stored as an associative array with the syntax ['title' => 'Book Name', 'author' => 'Author Name'].
Implement file reading and writing. Use file_get_contents() to read the JSON file and json_decode() to convert it to a PHP array. Use json_encode() and file_put_contents() to save new data. This is similar to Node.js's fs.readFileSync but built into PHP.
Display all books in a table using a foreach loop. Unlike JavaScript's forEach or map, PHP's foreach can iterate through both keys and values simultaneously with foreach ($books as $key => $book).
Add filtering functionality. Create a dropdown to filter by genre using array_filter(). This is PHP's equivalent to JavaScript's filter() method but with different syntax.
Implement sorting by rating or title. Use usort() for custom sorting logic. Add clickable column headers that trigger different sort orders.
Add delete functionality. Each book row should have a delete button that removes it from the array and updates the JSON file.
Handle concurrent access issues. Before writing, create a backup of the current file. Validate the JSON structure when reading to prevent corruption.
Add a toggle for read/unread status. Update the specific book in the array and save changes to the file.
Display statistics: total books, books read, average rating, most common genre. Use PHP array functions like array_reduce(), array_map(), and array_count_values().
Associative Arrays File I/O JSON Loops

Project Submission

03

Project: Functions & Code Organization

Vanilla PHP Course

Introduction

As your PHP applications grow, mixing all logic in one file becomes messy. Time to learn proper code organization with functions and file separation, similar to how you'd organize an Express app with routes and controllers.

Goal: Learn to organize code with functions and separate concerns

Assignment

Create a URL shortener application. Set up your project structure with separate folders for includes, data, and views. This mirrors the MVC pattern you'd use in Express.
Create a functions.php file for all your logic. Write functions for generating short codes (random 6-character alphanumeric strings), saving URLs, retrieving URLs, and tracking clicks. Use type hints like function saveUrl(string $longUrl): string.
Create a config.php file to store constants like your data file path, base URL, and short code length. Use define() or const for configuration values.
Build the main page with a form to submit long URLs. Use require_once to include your functions file at the top. This is PHP's version of import or require.
Implement the short code generation. Create a function that generates random codes and checks for collisions in your data file. If a collision occurs, regenerate. Use a while loop with a safety counter to prevent infinite loops.
Create a redirect handler. Make a redirect.php file that takes a short code from the URL (using $_GET['code']), looks up the long URL, increments the click count, and uses header('Location: ...') to redirect. Remember to call exit() after header redirects.
Build an analytics dashboard page. Display all shortened URLs with their click counts, creation dates, and last accessed timestamps. Format this in a nice table.
Add URL validation. Create a function using filter_var($url, FILTER_VALIDATE_URL) to ensure submitted URLs are valid before saving.
Implement a simple routing system. Check the URL path or GET parameters to determine which "page" to show, similar to Express routes but manual.
Use the null coalescing operator ?? throughout your code for default values. For example: $page = $_GET['page'] ?? 'home'. This is cleaner than using isset().
Functions Code Organization Routing Redirects

Project Submission

04

Project: Object-Oriented PHP Basics

Vanilla PHP Course

Introduction

You're familiar with JavaScript classes from your work with React and Node.js. PHP's OOP is similar but with some syntax differences and additional features like visibility modifiers and abstract classes.

Goal: Transition from procedural to OOP, similar to JavaScript classes

Assignment

Create a task management system using OOP principles. Start with a Task class that has properties for title, description, priority (high/medium/low), due date, and completion status.
Add a constructor using __construct(). Unlike JavaScript's constructor(), PHP uses magic methods with double underscores. Set default values for optional parameters.
Implement visibility modifiers. Make properties private and create public getter and setter methods. This enforces encapsulation better than JavaScript's approach.
Create a Project class that contains multiple tasks. Use composition: private array $tasks = [];. Add methods to add tasks, remove tasks, and get all tasks.
Implement method chaining. Make your setter methods return $this so you can chain calls like $task->setTitle('Title')->setPriority('high')->save().
Add static methods and properties. Create a static counter to auto-generate task IDs: private static int $nextId = 1; and public static function getNextId().
Implement a simple ORM-like pattern. Add save() and load() methods that serialize the object to JSON and store it. Use json_encode($this) to serialize objects.
Create a web interface with forms to create projects and add tasks to them. Display tasks grouped by project with the ability to mark them complete or delete them.
Add task dependencies. A task should be able to have prerequisite tasks that must be completed first. Implement this with an array of task IDs.
Implement class autoloading. Instead of requiring each class file manually, use spl_autoload_register() to automatically load class files when they're first used.
Classes OOP Encapsulation Composition

Project Submission

05

Project: MySQL Database Integration

Vanilla PHP Course

Introduction

JSON files work for learning, but real applications need databases. Time to replace file storage with MySQL using PDO, PHP's database abstraction layer. This is similar to using Mongoose with MongoDB, but for relational databases.

Goal: Replace file storage with MySQL using PDO

Assignment

Install MySQL or MariaDB if you haven't already. Create a database called task_manager. Design a schema with tables for projects and tasks with a foreign key relationship.
Create a database.php file that establishes a PDO connection. Unlike Mongoose's simple connect, PDO requires a DSN (Data Source Name): new PDO('mysql:host=localhost;dbname=task_manager', $user, $pass). Set error mode to exceptions.
Write a migration system. Create migrations folder with numbered SQL files. Build a simple PHP script that runs these migrations in order and tracks which ones have been applied.
Rebuild the task manager using MySQL instead of JSON files. Use prepared statements for all queries: $stmt = $pdo->prepare('SELECT * FROM tasks WHERE project_id = :id'). Never concatenate user input into SQL strings.
Implement CRUD operations using PDO. Create functions for inserting, selecting, updating, and deleting records. Use named parameters like :project_id instead of question marks for clarity.
Add JOIN queries to fetch projects with their associated tasks in one query. Use FETCH_ASSOC or FETCH_OBJ to get results as arrays or objects.
Implement search functionality. Build a search form that queries across multiple fields using LIKE clauses. Handle the search safely with prepared statements.
Add pagination. Limit results to 10 per page using LIMIT and OFFSET. Create previous/next page links and calculate total pages.
Implement proper error handling. Wrap database operations in try-catch blocks and display user-friendly messages instead of exposing database errors.
Use transactions for operations that modify multiple tables. Wrap related inserts/updates in $pdo->beginTransaction(), $pdo->commit(), and $pdo->rollBack() on error.
PDO MySQL Prepared Statements Migrations

Project Submission

06

Project: Sessions & User Authentication

Vanilla PHP Course

Introduction

Unlike Node.js where you'd use Passport or JWT, PHP has built-in session management. You'll learn how to implement authentication from scratch, understanding the fundamentals before using libraries.

Goal: Implement stateful user sessions and basic authentication

Assignment

Add a users table to your database with fields for id, email, password (hashed), and created_at. Add a user_id foreign key to the projects table.
Create registration and login pages. Registration should validate email format, check for existing users, hash passwords using password_hash() with BCRYPT algorithm, and insert the new user.
Implement login verification. Query the user by email, then use password_verify($inputPassword, $hashedPassword) to check credentials. Never store or compare plain-text passwords.
Set up sessions. Call session_start() at the beginning of every page that needs session access. Store user data in $_SESSION after successful login: $_SESSION['user_id'] = $userId.
Create an authentication middleware pattern. Make a function that checks if $_SESSION['user_id'] exists and redirects to login if not. Call this at the top of protected pages.
Implement logout functionality. Create a logout page that calls session_destroy() and redirects to the login page.
Make tasks user-specific. Modify all queries to filter by the logged-in user's ID. Users should only see their own projects and tasks.
Add "remember me" functionality. Set a secure cookie with a random token stored in the database. Check this cookie on each page load to auto-login users.
Implement session timeout. Track the last activity time and log users out after 30 minutes of inactivity.
Add user profile editing. Allow users to change their email and password. For password changes, require the current password for verification.
Sessions Authentication Password Hashing Security

Project Submission

07

Project: Advanced OOP & Design Patterns

Vanilla PHP Course

Introduction

Time to level up your OOP skills with inheritance, interfaces, and design patterns. You'll build a blog CMS that demonstrates professional PHP architecture patterns.

Goal: Learn inheritance, interfaces, and common design patterns

Assignment

Create an abstract Content class with common properties (id, title, body, author_id, created_at, updated_at) and abstract methods that child classes must implement.
Create Post and Page classes that extend Content. Posts have categories and tags, while Pages have parent-child relationships for navigation.
Create a Sluggable interface with a generateSlug() method. Both Post and Page should implement this to create URL-friendly slugs from titles.
Implement a Timestampable trait with methods for automatically setting created_at and updated_at. Use this trait in your content classes with the use keyword.
Build a Repository pattern. Create PostRepository and PageRepository classes that handle all database operations for their respective content types. This separates data access from business logic.
Use dependency injection. Pass the PDO connection to repositories through their constructors instead of creating it inside: public function __construct(PDO $db).
Implement namespaces to organize your classes. Put models in App\Models, repositories in App\Repositories, etc. Use PSR-4 autoloading.
Add publish/draft status to content. Create methods to publish and unpublish content. Only show published content to non-logged-in users.
Implement soft deletes. Instead of actually deleting records, set a deleted_at timestamp. Modify queries to exclude soft-deleted records by default.
Build a simple admin interface to create and manage posts and pages. Show a list of all content with edit and delete actions.
Abstract Classes Interfaces Inheritance Design Patterns

Project Submission

08

Project: RESTful API Development

Vanilla PHP Course

Introduction

You've built Express APIs before. Now build one in vanilla PHP to understand how frameworks like Laravel implement their API features under the hood.

Goal: Build a JSON API similar to Express, but in vanilla PHP

Assignment

Create a note-taking API. Build an api folder separate from your web pages. All responses should be JSON, no HTML rendering.
Build a Router class that maps HTTP methods and paths to handlers. Parse $_SERVER['REQUEST_METHOD'] and $_SERVER['REQUEST_URI'] to route requests.
Create helper functions for JSON responses. Build jsonResponse($data, $status = 200) that sets proper headers (Content-Type: application/json) and encodes data.
Implement JWT authentication. Use a library like Firebase JWT to generate and verify tokens. Return a token on login, then validate it on protected endpoints.
Read request bodies for POST/PUT requests. Unlike $_POST which only works for form data, read raw JSON from file_get_contents('php://input') and decode it.
Implement all CRUD endpoints: GET /notes, GET /notes/:id, POST /notes, PUT /notes/:id, DELETE /notes/:id. Use proper HTTP status codes (200, 201, 404, 400, 401, 500).
Add middleware pattern. Create functions that run before route handlers for authentication, logging, etc. Chain them together.
Implement request validation. Check required fields, data types, and format before processing. Return detailed validation errors with 400 status.
Add CORS headers for cross-origin requests: Access-Control-Allow-Origin, Access-Control-Allow-Methods, etc. Handle OPTIONS preflight requests.
Implement rate limiting. Track requests by IP address or user and limit to 100 requests per hour. Store counts in the database or use a file-based cache.
REST API JWT Routing Middleware

Project Submission

09

Project: File Uploads & Image Processing

Vanilla PHP Course

Introduction

Learn to handle file uploads securely and manipulate images using PHP's GD library. This is more complex than using npm packages in Node.js, giving you deeper understanding of file handling.

Goal: Handle file uploads, validation, and image manipulation

Assignment

Create an image gallery application. Set up an uploads folder with proper permissions (755) and add it to .gitignore.
Build a multiple file upload form with enctype="multipart/form-data" and multiple attribute. Access uploaded files through $_FILES superglobal.
Validate uploads thoroughly. Check file size (max 5MB), MIME type using mime_content_type(), and verify actual image format. Never trust client-provided filename or type.
Generate unique filenames to prevent overwriting. Use uniqid() or combine timestamp with random string. Keep original extension after validation.
Use PHP's GD library to generate thumbnails. Create multiple sizes (small: 150x150, medium: 400x400). Maintain aspect ratio using imagecopyresampled().
Extract EXIF data from images using exif_read_data(). Display camera model, settings, GPS coordinates if available. Handle missing data gracefully.
Organize images into albums stored in the database. Each album has a name and contains multiple images with descriptions.
Prevent directory traversal attacks. Never use user input directly in file paths. Validate and sanitize all path components.
Add a simple drag-and-drop upload interface using minimal JavaScript. Send files via AJAX to a PHP upload endpoint that returns JSON responses.
Implement watermarking. Add a semi-transparent text or image watermark to uploaded photos using imagettftext() or imagecopy().
File Uploads GD Library Image Processing Security

Project Submission

10

Project: Email & Background Processing

Vanilla PHP Course

Introduction

Learn to send emails and handle operations that shouldn't block HTTP requests. Unlike Node.js where everything is async, PHP requires different approaches for background tasks.

Goal: Send emails and handle time-consuming operations

Assignment

Install PHPMailer via Composer: composer require phpmailer/phpmailer. This is PHP's equivalent to Nodemailer.
Create a newsletter subscription system. Build a form to collect email addresses and store them in a subscribers table with email, token, verified, and subscribed_at fields.
Implement double opt-in verification. When someone subscribes, send a verification email with a unique token link. Only mark them as verified when they click the link.
Configure PHPMailer for SMTP. Set up with Gmail or a service like Mailtrap for testing. Never use PHP's built-in mail() function for production.
Create HTML email templates. Build welcome emails and newsletter templates with inline CSS (external stylesheets don't work in most email clients).
Implement a database-backed job queue. Create a jobs table to store pending email sends. When sending to many subscribers, queue jobs instead of sending immediately.
Build a worker script that processes queued jobs. This should be run via cron job every minute to process pending emails in batches.
Add email scheduling. Allow admins to compose newsletters and schedule them for future sending. Store scheduled send time in the database.
Implement retry logic for failed sends. Track attempt count and retry with exponential backoff. Mark as permanently failed after 3 attempts.
Create secure unsubscribe functionality. Generate unique tokens for each subscriber and provide one-click unsubscribe links in emails.
Email PHPMailer Queues Cron Jobs

Project Submission

11

Project: Security Hardening

Vanilla PHP Course

Introduction

Security is critical in PHP applications. Learn to protect against XSS, CSRF, SQL injection, and other common vulnerabilities. This knowledge applies to any backend technology.

Goal: Implement comprehensive security measures

Assignment

Take your blog CMS from project 7 and audit it for security issues. Document every place user input is accepted or output is displayed.
Implement XSS prevention. Use htmlspecialchars($data, ENT_QUOTES, 'UTF-8') on all output. Create a helper function. Never output raw user data to HTML.
Add CSRF protection to all forms. Generate tokens with bin2hex(random_bytes(32)), store in session, include as hidden input, and verify on submission.
Create a demonstration page showing SQL injection attacks and how prepared statements prevent them. Never use this code in production.
Implement input validation using filter_var() with appropriate filters (FILTER_VALIDATE_EMAIL, FILTER_VALIDATE_INT, etc). Validate types, formats, and ranges.
Add Content Security Policy headers to prevent XSS. Set Content-Security-Policy header that disallows inline scripts and limits resource origins.
Configure secure sessions. Set session.cookie_httponly = 1, session.cookie_secure = 1 (for HTTPS), and session.use_strict_mode = 1 in php.ini or via ini_set().
Implement login attempt limiting. Track failed login attempts by IP and email. Lock accounts temporarily after 5 failed attempts within 15 minutes.
Create a security audit log. Record all sensitive operations (logins, password changes, permission changes) with timestamps, IP addresses, and user agents.
Add two-factor authentication using TOTP (Time-based One-Time Password). Use a library like RobThree/TwoFactorAuth. Users should be able to enable 2FA and must enter a code after password.
XSS Prevention CSRF Input Validation Security Headers

Project Submission

12

Project: Full-Stack Application

Vanilla PHP Course

Introduction

This final project synthesizes everything you've learned. You'll build a complete project management application that could be used in production with some polish.

Goal: Combine all learned concepts into a complete application

Assignment

Plan your architecture. Design an MVC-like structure with folders for models, views, controllers, and libraries. Sketch your database schema with all relationships.
Build the authentication system with three user roles: admin (can do everything), member (can edit projects they're assigned to), and guest (read-only access). Store roles in a user_roles table.
Implement projects with multiple members. Create a many-to-many relationship between users and projects using a project_members junction table.
Build the task system with assignments. Tasks belong to projects and can be assigned to project members. Track status (todo, in progress, done), priority, and due dates.
Add a commenting system. Users can comment on tasks. Store comments with task_id, user_id, content, and created_at. Display them chronologically.
Implement file attachments for tasks. Allow uploading documents, images, etc. Store metadata in database, files on disk. Support multiple attachments per task.
Create an activity timeline. Log all important events (task created, status changed, comment added, file uploaded) in an activities table. Display a chronological feed.
Build a dashboard with statistics. Show total projects, tasks by status, upcoming deadlines, recent activity. Use aggregate SQL queries for counts and summaries.
Add REST API endpoints for AJAX operations. Create endpoints to update task status, add comments, etc. without page reloads. Return JSON responses.
Implement email notifications. Send emails when users are assigned tasks, when tasks are due soon, or when someone comments on their tasks. Use the queue system from project 10.
Add role-based permissions (ACL). Admins can create projects, members can only edit assigned projects, guests can only view. Check permissions before any sensitive operation.
Implement search with filters. Search across projects and tasks, filter by status, assignee, date range, etc. Build a dynamic query that adds WHERE clauses based on selected filters.
Generate PDF reports of project status using a library like TCPDF or mPDF. Include project details, task summary, and team members.
Add export functionality for CSV and JSON. Allow exporting tasks and projects in different formats for external use.
Create a simple CLI for admin tasks. Build a command-line script for creating users, assigning roles, and running maintenance tasks. Use $argv to parse arguments.
Full MVC ACL REST API File System Email Security

Next Steps After Completion:

Once you finish this project, you'll have a solid foundation in vanilla PHP. You're ready to explore Laravel (the most popular PHP framework), Symfony (enterprise-grade), API development with modern tooling, PHPUnit for testing, and DevOps practices with Docker and CI/CD.

Project Submission