I'm working on a PHP API for my angular JS Phonegap application. At the moment I'm focusing on working with a user's facebook public profile data.
I'll explain the process a little first:
- The user chooses to log in with facebook on the client side (using
phonegap's
cordova-plugin-facebook4
). - On successful facebook authentication there I retrieve the user's facebook "id", "picture", "name".
- I make a http POST request (from the client side) to an endpoint of my PHP API called auth (sending the facebook data with the request).
- I check if this "id" already exists in my facebook_users table of my database and if it does I return the facebook profile details back to the client side.
- If it does not exist I create a new row and insert the user's details (and also return the facebook profile details back to the client side in JSON format). (Note: I may also send back a JSON web token with the profile data but i have not implemented this.)
I was wondering what you think of my PHP code so far. I've not spent much time working with OOP so i'm wondering what you think of the structure I've created?
- Do you think my code is efficient?
- Is there anything you notice I could do better?
DatabaseConnection class
<?php
/*
* A class which contains all methods for retrieving and inserting data to our database.
*
*/
class DatabaseConnection{
private $query;
private $statement;
private static $instance = null;
private $pdoConnection;
public function __construct(){
//construct a new PDO object.
$this->pdoConnection = new PDO(HOSTDBNAME, USER, PASSWORD);
$this->pdoConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$this->pdoConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->pdoConnection->exec("SET CHARACTER SET utf8mb4");
}
public static function getInstance(){
if(!isset(self::$instance)){ // Check if the instance is not set.
//create a new DatabaseConnection instance which will execute the code in the above constructor.
self::$instance = new DatabaseConnection();
}
return self::$instance;
}
public function createNewFacebookUser($facebookUserID, $facebookName, $profilePicURL, $userPrivilegeID){
/*
* This method takes in a users facebook credentials as parameters and inserts them to our facebook_users table.
* If successfully inserted then we return an array containing the userID value.
* If unsuccessful return an exception
*
*/
$data = array();
try{
$this->query = "INSERT INTO facebook_users (facebookUserID, facebookName, profilePicURL, userPrivilegeID) VALUES (:facebookUserID, :facebookName, :profilePicURL, :userPrivilegeID)";
$this->statement = $this->pdoConnection->prepare($this->query);
$this->statement->bindValue(':facebookUserID', $facebookUserID, PDO::PARAM_STR);
$this->statement->bindValue(':facebookName', $facebookName, PDO::PARAM_STR);
$this->statement->bindValue(':profilePicURL', $profilePicURL, PDO::PARAM_STR);
$this->statement->bindValue(':userPrivilegeID', $userPrivilegeID, PDO::PARAM_INT);
$this->statement->execute();
$data['userID'] = $this->pdoConnection->lastInsertId();
return $data;
}catch(PDOException $e){
//Would it be a good idea to log this error in a php log file?
$data['exceptionOccurred'] = true;
$data['exceptionMessage'] = $e->getMessage();
return $data;
}
}
public function checkFacebookID($facebookUserID){
/*
* This method checks to see if a users facebook id exists in our facebook_users table.
*/
$data = array();
try{
$this->query = "SELECT userID FROM facebook_users WHERE facebookUserID = :facebookUserID LIMIT 1";
$this->statement = $this->pdoConnection->prepare($this->query);
$this->statement->bindValue(':facebookUserID', $facebookUserID, PDO::PARAM_STR);
$this->statement->execute();
$this->statement->setFetchMode(PDO::FETCH_ASSOC);
$rowCount = 0; //store the count into a local variable
while($row = $this->statement->fetch()){
//store the data from the result of the query into an associative array.
$data['userID'] = $row['userID'];
$rowCount++;
}
if($rowCount > 0){
//The facebookUserID exists in the database.
$data['facebookUserIDExists'] = true;
return $data;
}else{
$data['facebookUserIDExists'] = false;
return $data;
}
}catch(PDOException $e){
//Would it be a good idea to log this error in a php log file?
$data['exceptionOccurred'] = true;
$data['exceptionMessage'] = $e->getMessage();
return $data;
}
}
public function getUserProfile($userID){
//this method takes in a userID as a parameter and gets that user's data from the profiles table in the database
$data = array();
try{
$this->query ="SELECT * FROM facebook_users WHERE userID = :userID LIMIT 1";
$this->statement = $this->pdoConnection->prepare($this->query);
$this->statement->bindValue(':userID', $userID, PDO::PARAM_INT);
$this->statement->execute();
$this->statement->setFetchMode(PDO::FETCH_ASSOC);
while($row = $this->statement->fetch()){
//store the data from the result of the query into an associative array.
$data['userDetails'] = $row;
}
return $data;
}catch(PDOException $e){
$data['exceptionOccurred'] = true;
$data['exceptionMessage'] = $e->getMessage();
return $data;
}
}
}
?>
FacebookUser class
<?php
/*
* A class for working with a user's facebook public profile data.
*/
class FacebookUser {
private $databaseConnection;
private $facebookUserID;
private $facebookName;
private $profilePicURL;
private $userPrivilegeID = 1; //default value of 1 which is a regular user.
private $userID = null; //this will be the unique userID in our database.
private $exceptionOccurred;
public function __construct($facebookUserID, $facebookName, $profilePicURL, $userPrivilegeID = 1){
$this->databaseConnection = DatabaseConnection::getInstance();
$this->facebookUserID = $facebookUserID;
$this->facebookName = $facebookName;
$this->profilePicURL = $profilePicURL;
$this->userPrivilegeID = $userPrivilegeID;
}
public function checkFacebookDetails(){
/*This method checks to see if a users unique facebook ID already exist in our database.
*If it doesnt exist then we enter their details to the database and send their profile data back to the client side.
*If it does exist then we just send their profile data back to the client side.
*/
$data = array();
$checkIDResult = $this->databaseConnection->checkFacebookID($this->facebookUserID);
if($checkIDResult['facebookUserIDExists'] === false){
//create a new facebook user.
$data = $this->databaseConnection->createNewFacebookUser($this->facebookUserID, $this->facebookName, $this->profilePicURL, $this->userPrivilegeID);
$this->userID = $data['userID'];
}else{
$this->userID = $checkIDResult['userID'];
}
//return the users facebook details (which are stored in our database).
return $this->getFacebookUserProfile($this->userID);
}
public function getFacebookUserProfile($userID){
//This method takes in a userID as a parameter and gets a users profile data from our database
$data = array();
$data = $this->databaseConnection->getUserProfile($userID);
return $data;
}
public function getFacebookName(){
return $this->facebookName;
}
public function getUserID(){
return $this->userID;
}
}
?>
MyAPI class (for handling the endpoints of our API)
<?php
require_once 'config.php';
require_once 'API.class.php';
require_once 'DatabaseConnection.class.php';
require_once 'FacebookUser.class.php';
require_once 'Validator.class.php';
class MyAPI extends API {
protected $FacebookUser;
protected $Validator;
public function __construct($request, $origin) {
//This contructor takes in the super global $_REQUEST array as a parameter which should contain a request and apiKey index
//and also the $_SERVER['HTTP_ORIGIN']
parent::__construct($request['request']);
//Should also have functionality to process an API key which should be sent with any requests from the client side application.
}
/**
* auth Endpoint
* A HTTP POST request is performed on the client side to this endpoint and a users facebook data is sent with the request.
* We firstly check if the data is valid (a valid length and valid encoding-methods defined in Validator class).
* We also filter the data for security.
* We then process the data to see if it exists in our database.
* Once this is done we return the users facebook profile details in JSON format.
*/
protected function auth() {
if ($this->method == 'POST') {
//we need to get the data the following way because we have the Content-Type header set to application/json, so $_POST will no longer be populated
$rawPostData = file_get_contents('php://input');
$json = json_decode($rawPostData);
$facebookUserID = $json->facebookUserID;
$facebookName = $json->facebookName;
$profilePicURL = $json->profilePicURL;
$filteredInputs = array(); //an array to hold the filtered inputs
//create an instance of the Validator class
$this->Validator = new Validator();
//Check if the inputs are a valid length and also valid utf8 encoding
$inputsAreValid = $this->Validator->checkInputsAreValid(array(array("input" => $facebookUserID, "minLength" => 10, "maxLength" => 30),
array("input" => $facebookName, "minLength" => 2, "maxLength" => 40),
array("input" => $profilePicURL, "minLength" => 10, "maxLength" => 250)));
if($inputsAreValid){
//If the inputs are a valid length and also valid utf8 then filter them.
$filteredInputs = $this->Validator->filterInputs(array(array("input" => $facebookUserID, "filterMethod" => FILTER_SANITIZE_NUMBER_INT),
array("input" => $facebookName, "filterMethod" => FILTER_SANITIZE_STRING),
array("input" => $profilePicURL, "filterMethod" => FILTER_SANITIZE_STRING)));
}
//create an instance of the FacebookUser class
$this->FacebookUser = new FacebookUser($filteredInputs[0], $filteredInputs[1], $filteredInputs[2]);
$facebookProfileData = $this->FacebookUser->checkFacebookDetails();
return json_encode($facebookProfileData);
} else {
$errorMessage = array("error" => "Only accepts POST requests");
return json_encode($errorMessage);
}
}
}
?>