Read this in other languages: English, Français
It's a tiny social network where USERs can POST messages and COMMENT these messages.
This project was done for my PHP courses. The goal is to review the PHP syntax and use PDO (PHP Data Objects) for accessing databases.
The structure is really simple:
It's a simple MVC implementation without OOP (Object-Oriented Programming) in PHP .
+---config/
| data.sql
| structure.sql
|
+---controllers/
| controller.php
|
+---models/
| PDO.php
| PostManager.php
| CommentManager.php
| UserManager.php
|
+---views/
| DisplayPosts.php
| LoginForm.php
| RegisterForm.php
|
\---web/
| index.php <----- entry point
|
+---css/
+---img/
\---js/
These instructions will get you a copy of the project up and running on your local machine.
You need to have a L.A.M.P. architecture working on your computer.
Else you need to install the missing parts (instructions for Ubuntu below).
MySQL server:
Installation
sudo apt-get install mysql-serverStart MySQL server
sudo systemctl start mysqlCreate a new MySQL user. Of course, you can change the queries to replace dbuser & 1234Soleil! with your own username & password, just don't forget what you enter because you will need them after.
sudo mysqlCREATE USER 'dbuser'@'localhost' IDENTIFIED BY '1234Soleil!';
GRANT ALL PRIVILEGES ON * . * TO 'dbuser'@'localhost';
EXIT;Apache2 server:
Installation
sudo apt-get install apache2Start the server:
sudo systemctl start apache2PHP & PDO:
Installation
sudo apt update
sudo apt install php php-cli php-mysqlRestart apache2:
sudo systemctl restart apache2-
Fork this repo and clone it on your computer inside the DocumentRoot of apache (by default it should be inside /var/www/html).
cd /var/www/html git clone https://github.com/YOUR-USERNAME/socialnetwork -
Create an populate the database:
Go into the config directory and run the 2 SQL scripts (replace dbuser by the user you entered before)
cd socialnetwork/config mysql -u dbuser -p < structure.sql mysql -u dbuser -p < data.sql
-
You can connect to the database tsn to see if the database and the table were well created
sudo mysql
use tsn; show tables; select * from user; exit;
-
-
Go into the models directory and modify PDO.php to set the right credentials ($db_user & $db_passwd).
<?php
$db_user = "dbuser";
$db_passwd = "1234Soleil!";
$db_host = "localhost";
$db_port = "3306";
$db_name = "tsn";
$db_dataSourceName = "mysql:host=$db_host;port=$db_port;dbname=$db_name";
$PDO = new PDO($db_dataSourceName, $db_user, $db_passwd);
$PDO->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
?>-
Now if you go on your web browser you should see something:
The posts are dynamically retrieved from the database, nonetheless, the comments are hardcoded.
Final Goal: You have to retrieve these comments dynamically for each post.
Have a look inside the project to understand approximately each line of code. Start with the entry point, the index.php inside the web directory. And follow the execution to understand the whole project.
<?php
session_start();
include "../controllers/controller.php";Never work on the master branch, so start to create a develop branch from master and push it to GitHub.
git branch develop
git checkout develop
git push origin developAs you will work on the feature to retrieve comments, create a new branch feature/retrieveComments from develop and push it to GitHub.
git branch feature/retrieveComments
git checkout feature/retrieveComments
git push origin feature/retrieveCommentsNow you are ready to code.
If you have a look to controllers/controller.php you'll see some fake data about comments:
case 'display':
include "../models/PostManager.php";
$posts = GetAllPosts();
include "../models/CommentManager.php";
$comments = array();
// ===================HARDCODED PART===========================
$comments[1] = array(
array("nickname" => "FakeUser1", "created_at" => "1970-01-01 00:00:00", "content" => "Fake comment 01."),
array("nickname" => "FakeUser2", "created_at" => "1970-01-02 00:00:00", "content" => "Fake comment 02."),
array("nickname" => "FakeUser1", "created_at" => "1970-01-03 00:00:00", "content" => "Fake comment 03.")
);
$comments[3] = array(
array("nickname" => "FakeUser1", "created_at" => "1970-01-01 00:00:00", "content" => "Fake comment 04."),
);
// =============================================================
include "../views/DisplayPosts.php";
break;You have to:
- Go into models/CommentManager.php to create a new function GetAllCommentsFromPostId. This new function should return all comments from a post id. Don't forget you need the nickname of the user who commented. To do that, have a look to the GetAllCommentsFromUserId. Your new function do almost the same thing but instead of "WHERE comment.user_id = $userId " you should have "WHERE comment.post_id = $postId ".
function GetAllCommentsFromPostId($postId) {
// Code here...
}-
Inside controllers/controller.php remove the hardcoded part but keep $comments = array().
-
Instead, loop over $posts and call your new function GetAllCommentsFromPostId for each post giving the post id.
-
Inside the loop, we need to fill the $comments array. $comments is an associative array. The "key" should be the post id. The "value" is the result of your function GetAllCommentsFromPostId.
When all is ok, commit your changes and push your work to GitHub.
You can merge your feature branch into develop and push develop too !!!
git commit -a -m "Remove the hardcoded comments"
git push origin feature/retrieveComments
git checkout develop
git merge feature/retrieveComments
git push origin developFinal Goal: Made a simple search feature.
You should be on the develop branch else enter this line.
git checkout developCreate a new branch feature/search from develop and push it to GitHub.
git branch feature/search
git checkout feature/search
git push origin feature/searchNow you are ready to code.
Modify the file: views/DisplayPosts.php
Before the "Login" and "Sign Up" links, add the search form like this:
<ul class="navbar-nav">
<li class="nav-item">
<form class="nav-link" method="get">
<input name="search" type="text"></input>
</form>
</li>
<li class="nav-item">
<a class="nav-link" href="?action=login" role="button">Login</a>
</li>
<li class="nav-item">
<a class="nav-link" href="?action=register" role="button">Sign Up</a>
</li>
</ul>-
Modify the file: controllers/controller.php
If the search parameter is well defined into $_GET we will use it to retrieve the right posts.
Else the posts will continue to come from the GetAllPosts function.
case 'display':
default:
include "../models/PostManager.php";
if (isset($_GET['search'])) {
$posts = SearchInPosts($_GET['search']);
} else {
$posts = GetAllPosts();
}
include "../models/CommentManager.php";-
You have to write the SearchInPosts function into models/PostManager.php.
To do that copy/paste the GetAllPosts function, rename it into SearchInPosts.
-
Add the argument $search to this function.
-
Add a where clause into the sql query using this argument, it should look like this:
"SELECT post.*, user.nickname "
. "FROM post LEFT JOIN user on (post.user_id = user.id) "
. "WHERE content like '%$search%' "
. "ORDER BY post.created_at DESC"When your search works well, commit your changes and push your work to GitHub.
You can merge your feature branch into develop and push develop too !!!
git commit -a -m "Simple search using the sql like"
git push origin feature/search
git checkout develop
git merge feature/search
git push origin developFinal Goal: Made a simple login & logout feature.
You should be on the develop branch else enter this line.
git checkout developCreate a new branch feature/login from develop and push it to GitHub.
git branch feature/login
git checkout feature/login
git push origin feature/loginNow you are ready to code.
When the user will be logged, $_SESSION['userId'] will contain his user id.
Modify the file: views/DisplayPosts.php
The content of the navbar-nav section depends if the variable $_SESSION['userId'] is set or not.
<ul class="navbar-nav">
<li class="nav-item">
<form class="nav-link" method="get">
<input name="search" type="text"></input>
</form>
</li>
<?php
if (isset($_SESSION['userId'])) {
?>
<li class="nav-item">
<a class="nav-link" href="?action=logout" role="button">Logout</a>
</li>
<?php
} else {
?>
<li class="nav-item">
<a class="nav-link" href="?action=login" role="button">Login</a>
</li>
<li class="nav-item">
<a class="nav-link" href="?action=register" role="button">Sign Up</a>
</li>
<?php
}
?>
</ul>
-
Modify the file: controllers/controller.php
The logout action unset $_SESSION['userId'] and redirect the visitor to the display action.
The login action try to get the user id from his username & password.
If it succeed, it sets the user id into the $_SESSION['userId'] and redirect the visitor to the display action.
If it failed, it set an error message and display the login form again.
case 'logout':
if (isset($_SESSION['userId'])) {
unset($_SESSION['userId']);
}
header('Location: ?action=display');
break;
case 'login':
include "../models/UserManager.php";
if (isset($_POST['username']) && isset($_POST['password'])) {
$userId = GetUserIdFromUserAndPassword($_POST['username'], $_POST['password']);
if ($userId > 0) {
$_SESSION['userId'] = $userId;
header('Location: ?action=display');
} else {
$errorMsg = "Wrong login and/or password.";
include "../views/LoginForm.php";
}
} else {
include "../views/LoginForm.php";
}
break;- You have to write the GetUserIdFromUserAndPassword function into models/UserManager.php. This function takes 2 arguments $username and $password and it has to return the user id (if the user well exists and his password is the same) else this function has to return -1.
When your login/logout works well, commit your changes and push your work to GitHub.
You can merge your feature branch into develop and push develop too !!!
git commit -a -m "Simple login/logout system"
git push origin feature/login
git checkout develop
git merge feature/login
git push origin developFinal Goal: Be able to post a new message on the tiny social network.
You should be on the develop branch else enter this line.
git checkout developCreate a new branch feature/newmsg from develop and push it to GitHub.
git branch feature/newmsg
git checkout feature/newmsg
git push origin feature/newmsgNow you are ready to code.
Modify the file: views/DisplayPosts.php
When the user is logged (when the variable $_SESSION['userId'] is set) display the form.
<?php
if (isset($_SESSION['userId'])) {
?>
<div class="row newMsg">
<div class="col">
<form class="input-group" method="POST" action="?action=newMsg">
<input name="msg" class="form-control" placeholder="Add a message" type="text">
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
</div>
<?php
}
?>
-
Modify the file: controllers/controller.php
If the user is logged and want to post a new message (value read from $_POST['msg']) so we will create an insert into the post table with the function CreateNewPost.
case 'newMsg':
include "../models/PostManager.php";
if (isset($_SESSION['userId']) && isset($_POST['msg'])) {
CreateNewPost($_SESSION['userId'], $_POST['msg']);
}
header('Location: ?action=display');
break;-
You have to write the CreateNewPost function into models/PostManager.php.
This function takes 2 arguments $userId and $msg. It has to execute this query:
"INSERT INTO post(user_id, content) values ($userId, '$msg')"When you are to able to post a new message, commit your changes and push your work to GitHub.
You can merge your feature branch into develop and push develop too !!!
git commit -a -m "Simple post system"
git push origin feature/newmsg
git checkout develop
git merge feature/newmsg
git push origin developFinal Goal: Prevent for sql injections.
You should be on the develop branch else enter this line.
git checkout developCreate a new branch feature/security from develop and push it to GitHub.
git branch feature/security
git checkout feature/security
git push origin feature/securityNow you are ready to code.
The current version can be hacked when posting a new message because the input of the user is directly put inside the sql query. You have to use prepared statements/queries to prevent sql injections.
TODO Replace in the 3 managers (CommentManager, UserManager, PostManager) the sql queries that use input data by prepared queries.
See the example below for the CreateNewPost function of the PostManager.
Before:
function CreateNewPost($userId, $msg)
{
global $PDO;
$response = $PDO->exec("INSERT INTO post(user_id, content) values ($userId, '$msg')");
}After:
function CreateNewPost($userId, $msg)
{
global $PDO;
$response = $PDO->prepare("INSERT INTO post(user_id, content) values (:userId, :msg)");
$response->execute(
array(
"userId" => $userId,
"msg" => $msg
)
);
}Another example for the GetOneCommentFromId function of the CommentManager.
Before:
function GetOneCommentFromId($id)
{
global $PDO;
$response = $PDO->query("SELECT * FROM comment WHERE id = " . $id);
return $response->fetch();
}After:
function GetOneCommentFromId($id)
{
global $PDO;
$response = $PDO->prepare("SELECT * FROM comment WHERE id = :id ");
$response->execute(
array(
"id" => $id
)
);
return $response->fetch();
}Another example for the SearchInPosts function of the PostManager.
Before:
function SearchInPosts($search)
{
global $PDO;
$response = $PDO->query(
"SELECT post.*, user.nickname "
. "FROM post LEFT JOIN user on (post.user_id = user.id) "
. "WHERE content like '%$search%' "
. "ORDER BY post.created_at DESC"
);
return $response->fetchAll();
}After:
function SearchInPosts($search)
{
global $PDO;
$response = $PDO->prepare(
"SELECT post.*, user.nickname "
. "FROM post LEFT JOIN user on (post.user_id = user.id) "
. "WHERE content like :search "
. "ORDER BY post.created_at DESC"
);
$searchWithPercent = "%$search%";
$response->execute(
array(
"search" => $searchWithPercent
)
);
return $response->fetchAll();
}If all the features are still working, commit your changes and push your work to GitHub.
You can merge your feature branch into develop and push develop too !!!
git commit -a -m "Prevent SQL Injection"
git push origin feature/security
git checkout develop
git merge feature/security
git push origin developFinal Goal: Made a simple registration process.
You should be on the develop branch else enter this line.
git checkout developCreate a new branch feature/registration from develop and push it to GitHub.
git branch feature/registration
git checkout feature/registration
git push origin feature/registrationNow you are ready to code.
- Modify the file: controllers/controller.php
case 'register':
include "../models/UserManager.php";
if (isset($_POST['username']) && isset($_POST['password']) && isset($_POST['passwordRetype'])) {
$errorMsg = NULL;
if (!IsNicknameFree($_POST['username'])) {
$errorMsg = "Nickname already used.";
} else if ($_POST['password'] != $_POST['passwordRetype']) {
$errorMsg = "Passwords are not the same.";
} else if (strlen(trim($_POST['password'])) < 8) {
$errorMsg = "Your password should have at least 8 characters.";
} else if (strlen(trim($_POST['username'])) < 4) {
$errorMsg = "Your nickame should have at least 4 characters.";
}
if ($errorMsg) {
include "../views/RegisterForm.php";
} else {
$userId = CreateNewUser($_POST['username'], $_POST['password']);
$_SESSION['userId'] = $userId;
header('Location: ?action=display');
}
} else {
include "../views/RegisterForm.php";
}
break;- You have to write the IsNicknameFree and CreateNewUser functions into UserManager.php.
function IsNicknameFree($nickname)
{
global $PDO;
$response = $PDO->prepare("SELECT * FROM user WHERE nickname = :nickname ");
$response->execute(
array(
"nickname" => $nickname
)
);
return $response->rowCount() == 0;
}
function CreateNewUser($nickname, $password)
{
global $PDO;
$response = $PDO->prepare("INSERT INTO user (nickname, password) values (:nickname , :password )");
$response->execute(
array(
"nickname" => $nickname,
"password" => $password
)
);
return $PDO->lastInsertId();
}When your registration process works well, commit your changes and push your work to GitHub.
You can merge your feature branch into develop and push develop too !!!
git commit -a -m "Simple registration process"
git push origin feature/registration
git checkout develop
git merge feature/registration
git push origin developFinal Goal: Don't store the passwords like this use md5
Store clear text passwords is not a good idea. You have to hash them, for example with the MD5 function.
Update the current passwords of your database with this SQL query (run this only once !!!):
UPDATE user SET password = MD5(password);You should be on the develop branch else enter this line.
git checkout developCreate a new branch feature/md5 from develop and push it to GitHub.
git branch feature/md5
git checkout feature/md5
git push origin feature/md5Now you are ready to code.
The login doesn't work anymore because the passwords are now stored in md5 format and we compare the given password without md5, so you have to modify the GetUserIdFromUserAndPassword function into UserManager.php to fix it
function GetUserIdFromUserAndPassword($username, $password)
{
global $PDO;
$response = $PDO->prepare("SELECT id FROM user WHERE nickname = :username AND password = MD5(:password) ");
$response->execute(
array(
"username" => $username,
"password" => $password
)
);
if ($response->rowCount() == 1) {
$row = $response->fetch();
return $row['id'];
} else {
return -1;
}
}The registration doesn't work as it should. The passwords have to be stored in md5 format so you have to modify the CreateNewUser function into UserManager.php
function CreateNewUser($nickname, $password)
{
global $PDO;
$response = $PDO->prepare("INSERT INTO user (nickname, password) values (:nickname , MD5(:password) )");
$response->execute(
array(
"nickname" => $nickname,
"password" => $password
)
);
return $PDO->lastInsertId();
}When your registration & login process work well using md5, commit your changes and push your work to GitHub.
You can merge your feature branch into develop and push develop too !!!
git commit -a -m "Use md5 for the passwords"
git push origin feature/md5
git checkout develop
git merge feature/md5
git push origin developFinal Goal: Be able to post a new comment on any post.
You should be on the develop branch else enter this line.
git checkout developCreate a new branch feature/newcomment from develop and push it to GitHub.
git branch feature/newcomment
git checkout feature/newcomment
git push origin feature/newcommentNow you are ready to code.
Modify the file: views/DisplayPosts.php
When the user is logged (when the variable $_SESSION['userId'] is set) display the form to comment.
<?php
if (isset($_SESSION['userId'])) {
?>
<div class="input-group">
<form class="input-group" method="POST" action="?action=newComment">
<input name="postId" type="hidden" value="<?= $onePost['id'] ?>">
<input name="comment" class="form-control" placeholder="Add a comment" type="text">
<span class="input-group-text">
<a href="#" onclick="$(this).closest('form').submit()"><i class="fa fa-edit"></i></a>
</span>
</form>
</div>
<?php
}
?>
-
Modify the file: controllers/controller.php
If the user is logged and want to post a new message (values read from $_POST['msg'] & $_POST['postId']) so we will create an insert into the post table with the function CreateNewPost.
case 'newComment':
include "../models/CommentManager.php";
if (isset($_SESSION['userId']) && isset($_POST['postId']) && isset($_POST['comment'])) {
CreateNewComment($_SESSION['userId'], $_POST['postId'], $_POST['comment']);
}
header('Location: ?action=display');
break;-
You have to write the CreateNewComment function into models/CommentManager.php.
This function takes 3 arguments $userId, $postId and $comment. It has to execute this query:
"INSERT INTO comment(user_id, post_id, content) values (:userId, :postId, :comment)"When you are to able to post a new comment, commit your changes and push your work to GitHub.
You can merge your feature branch into develop and push develop too !!!
git commit -a -m "Simple comment system"
git push origin feature/newcomment
git checkout develop
git merge feature/newcomment
git push origin develop



