
Table Of Content
1 Prerequisites
- PHP 7.4+
- Apache/Nginx web server
- MySQL/MariaDB
- Writable uploads/ folder (chmod 755 or 777)
2 Introduction
3 Create Project and Config Database
3.1 Create Project Folder "ckeditor-app"
3.2 Configure MySql Database
dbconfig.php (Database Configuration)
In this dbconfig.php file, database setting variables are defined.
<?php
// Database settings
define('DB_HOST', 'localhost');
define('DB_USERNAME', 'root');
define('DB_PASSWORD', '');
define('DB_NAME', 'blog');
?>
Database Table Creation (Run this SQL in your MySQL tool, e.g., phpMyAdmin)
CREATE TABLE `posts` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`title` VARCHAR(255) NOT NULL,
`description` TEXT,
`created` DATETIME DEFAULT CURRENT_TIMESTAMP,
`updated` DATETIME DEFAULT NULL,
`status` TINYINT(4) DEFAULT 1,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
4 Include CKEditor 5
<script src="https://cdn.ckeditor.com/ckeditor5/43.0.0/classic/ckeditor.js"></script>
5 Create index.php File (Main Editor Form)
<?php
session_start();
include 'dbconfig.php';
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CKEditor 5 Image Upload PHP Example</title>
<!-- Bootstrap 5 CDN for styling -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<!-- CKEditor 5 CDN -->
<script src="https://cdn.ckeditor.com/ckeditor5/43.0.0/classic/ckeditor.js"></script>
<style>
.ck-editor__editable { min-height: 400px; }
</style>
</head>
<body>
<div class="container mt-5">
<?php if (isset($_SESSION['message'])): ?>
<div class="alert alert-success"><?php echo $_SESSION['message']; unset($_SESSION['message']); ?></div>
<?php endif; ?>
<h2 class="mb-4">Create Post with CKEditor 5</h2>
<form method="POST" action="store.php">
<div class="mb-3">
<label for="title" class="form-label">Title</label>
<input type="text" class="form-control" id="title" name="title" required>
</div>
<div class="mb-3">
<label for="editor" class="form-label">Content</label>
<textarea name="description" id="editor"></textarea>
</div>
<button type="submit" class="btn btn-primary">Save Post</button>
</form>
</div>
<script>
ClassicEditor
.create(document.querySelector('#editor'), {
ckfinder: {
uploadUrl: 'upload.php' // Your custom PHP upload endpoint
},
toolbar: ['heading', '|', 'bold', 'italic', 'link', 'bulletedList', 'numberedList', 'blockQuote', 'insertImage', 'undo', 'redo']
})
.catch(error => console.error(error));
</script>
<!-- Bootstrap JS (optional for interactivity) -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
</body>
</html>
6 Create upload.php File (Image Upload Handler)
<?php
header('Content-Type: application/json');
if (!empty($_FILES['upload'])) {
$file = $_FILES['upload'];
$targetDir = "uploads/";
if (!is_dir($targetDir)) {
mkdir($targetDir, 0755, true);
}
$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
$allowed = ['jpg', 'jpeg', 'png', 'gif'];
if (!in_array($ext, $allowed)) {
echo json_encode(['uploaded' => 0, 'error' => ['message' => 'Invalid file type. Only JPG, JPEG, PNG, GIF allowed.']]);
exit;
}
// Secure filename to prevent collisions/overwrites
$newName = md5(time() . $file['name']) . '.' . $ext;
$targetFile = $targetDir . $newName;
if (move_uploaded_file($file['tmp_name'], $targetFile)) {
// Adjust URL to match your server path (e.g., if in subfolder, prepend accordingly)
$url = '/' . basename(__DIR__) . '/' . $targetFile;
echo json_encode([
'uploaded' => 1,
'fileName' => $file['name'],
'url' => $url
]);
} else {
echo json_encode(['uploaded' => 0, 'error' => ['message' => 'File upload failed. Check server permissions.']]);
}
} else {
echo json_encode(['uploaded' => 0, 'error' => ['message' => 'No file received.']]);
}
?>
7 Create store.php File
<?php
session_start();
include 'dbconfig.php';
$conn = new mysqli(DB_HOST, DB_USERNAME, DB_PASSWORD, DB_NAME);
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
if (isset($_POST['title']) && isset($_POST['description'])) {
$stmt = $conn->prepare("INSERT INTO posts (title, description) VALUES (?, ?)");
$stmt->bind_param("ss", $_POST['title'], $_POST['description']);
if ($stmt->execute()) {
$_SESSION['message'] = 'Post created successfully!';
} else {
$_SESSION['message'] = 'Error saving post: ' . $stmt->error;
}
$stmt->close();
} else {
$_SESSION['message'] = 'Invalid form data.';
}
$conn->close();
header("Location: index.php");
exit;
?>
8 Folder Structure
9 Testing Steps
- Place all files in your project folder.
- Ensure uploads/ is created and writable.
- Update dbconfig.php with your actual DB credentials.
- Run the SQL to create the table.
- Open index.php in your browser.
- Enter a title, add content in the editor, upload an image via the image toolbar icon (or drag-and-drop).
- Submit – it should save to the DB and show a success message.
- Check the posts table for the entry (images are stored in uploads/, paths in the description HTML).
10 Conclusion
Written by Revathi M
PHP Developer & Technical Writer · 10+ years building web applications with CodeIgniter and Laravel
Revathi specializes in PHP backend development, authentication systems, and REST API design. She writes practical, production-tested tutorials at Get Sample Code to help developers build secure applications faster.
Frequently Asked Questions
You need PHP version greater than 7.4, a web server (e.g., Apache/Nginx), and a MySQL database.
Create a database, then run the provided SQL to create the `posts` table with fields: id, title, description, created, updated, status.
Add the CDN script in the `<head>`: <script src=`https://cdn.ckeditor.com/ckeditor5/41.4.2/classic/ckeditor.js`> </script> (version may vary).
Use `ClassicEditor.create(document.querySelector('#editor'), { ckfinder: { uploadUrl: 'upload.php' } })`.
It handles image uploads from CKEditor/CKFinder, moves the file to the `uploads/` folder with a unique name, and returns a JSON response with the URL.
CKFinder sends the file as `upload`, but the tutorial uses `uploadedfile`. Update to `$_FILES['upload']` to match standard CKFinder behavior.
Images are saved in the `uploads/` directory with a unique filename (MD5 hash + original extension).
The form submits to `store.php`, which inserts the title and description (including img tags with uploaded image URLs) into the `posts` table.
Ensure the `uploads/` directory exists in the project root and has write permissions (e.g., 755 or 777) for the web server user.
Verify `upload.php` returns proper JSON ({`uploaded`:1,`url`:`path/to/image.jpg`}), check file field name mismatch, and ensure no PHP errors (enable error reporting).
No, it uses direct `$_REQUEST` concatenation. For production, switch to prepared statements or PDO to prevent SQL injection.
The built-in CKFinder plugin (enabled via `ckfinder: { uploadUrl }`) provides a browse server feature automatically.
Yes, modify the `$target_path` in `upload.php` and add validation for extensions/size.
Place files in your web root, create the `uploads/` folder (writable), set up the database, visit `index.php`, add content/images, and save.
