I am working on a form for a friend. When a user submits the form their IP address is added into a database table. Every time a user then visits the form I run a check to see if their IP address is already in the table. If it is then they have already submitted the form.
I did this previously but decided to change how it works and now when I got to run any queries or connect to the database the whole page goes blank.
Here is my database class (class.Database.inc.php):
<?php
/**
* MySQLi database; only one connection is allowed.
*/
class Database {
private $_connection;
// Store the single instance.
private static $_instance;
/**
* Get an instance of the Database.
* #return Database
*/
public static function getInstance() {
if (!self::$_instance) {
self::$_instance = new self();
}
return self::$_instance;
}
/**
* Constructor.
*/
public function __construct() {
$this->_connection = new mysqli('localhost', 'MHP_TICKET_ADMIN', 'fZx_142n', 'MHP_TICKET_SYS');
// Error handling.
if (mysqli_connect_error()) {
trigger_error('Failed to connect to MySQL: ' . mysqli_connect_error(), E_USER_ERROR);
}
}
?>
The code at the top of the form file (index.php):
<?php
require_once('class.Database.inc.php');
// Check database to see if the user has already submitted.
$user_ip = $_SERVER['REMOTE_ADDR'];
$db = Database::getInstance();
$mysqli = $db->getConnection();
$sql_query = "SELECT ip FROM ip_address WHERE ip = '$user_ip'";
$result = $mysqli->query($sql_query);
if ($row = $result->fetch_assoc()) {
die('You have already placed your submission.');
}
?>
EDIT
I entered the credentials in the wrong order, and it took me 2 hours to figure that out...
I know you've fixed this now (and I would comment, but I've not quite reached 50 yet) but for future reference this may help - an unexpected blank page in PHP most likely means an error is being thrown.
I make a habit of including error_reporting(-1) at the top of my script (in this case, your class.Database.inc.php file) so I see those errors immediately while developing and debugging. Switch this to error_reporting(0) when you go live of course to make sure errors are hidden from end users.
Related
I am brand new to PHP and have a simple project, I do Java.
In my simple project I have some html files and some Php files.
I connect to a device via SSH in my Php web page, and after that I am gonna run some commands and return back SSH returned data.
Any ways, So I designed a Html file for Login and use that in my php, here is my Php code :
<?php
class Connection {
public static $ip; // I even made static this variable to test if I can access Static variable from other
public $username;
public $password;
public static $ssh;
public function sshConnection() {
include ('./view/login.html'); // The html page contain variables
include('Net/SSH2.php'); // I use phpseclib to connect via SSH
if(isset($_POST['lgin'], $_POST['ip'], $_POST['username'], $_POST['password'])) { // Login button in html file
$this->ip = $_POST['ip']; // input type to get ip in html file
$this->username = $_POST['username']; // input type to get username in html file
$this->password = $_POST['password']; // input type to get password in html file
$this->ssh = new Net_SSH2($this->ip);
if (!$this->ssh->login($this->username, $this->password)) {
print('Login faild');
} else {
header("Location: http://localhost/wireless/configwireless.php"); // This redirect to next page that I should display some Commands
}
}
}
}
$connection=new Connection();
$connection->sshConnection();
?>
I need $ssh variable in next page so that I can run commands via this connection and make sessions.
I googled and find out I can access the static variable via this code :
classname::$variableName;
I even made my $ip variable static to test if I can access that or not, But no chance,
Here is my configwireless.php Code :
<?php
echo Connection::$ip; // Does not display the input ip variable.
?>
But it does not display the $ip variable.
Where I am doing wrong?
For assignment static property in class, should be used self or static keyword. So instead of
$this->ip = $_POST['ip'];
use this
static::$ip = $_POST['ip'];
or
self::$ip = $_POST['ip'];
As my dear friend #Mohammad notice, after header("Location: http://localhost/wireless/configwireless.php"); variables will be lost.
I did this before header("Location: http://localhost/wireless/configwireless.php");:
session_start();
$_SESSION['ip'] = $this->ip;
And in next page I added :
session_start();
$x=$_SESSION['ip'];
echo $x;
I wrote a login script for a website that I am building using resources I have found online. When I ran my code on a local server it worked fine but now that I am actually running it online on a real server it doesn't work. I think I have narrowed down my error but with being new to PHP and not having prior experience with MySql I can't really fix my problem. This is the file for the login script:
//login file
<?php
class Login{
private $db_connection = null;
public function __construct(){
session_start();
$this->dologinWithPostData();
}
private function dologinWithPostData(){
$this->db_connection = new mysqli(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);
if (!$this->db_connection()->connect_errno) {
// escape the POST stuff
$email = $_POST['email'];
// database query, getting all the info of the selected user (allows login via email address in the
// username field)
$sql = "SELECT email, password
FROM users
WHERE email = '" . $email ."'";
$result_of_login_check = $this->db_connection->query($sql);//This is 0
// if this user exists
if ($result_of_login_check->num_rows == 1) {
// get result row (as an object)
$result_row = $result_of_login_check->fetch_object();
// using PHP 5.5's password_verify() function to check if the provided password fits
// the hash of that user's password
if ($_POST['password'] == $result_row->password) {
// write user data into PHP SESSION (a file on your server)
$_SESSION['email'] = $result_row->email;
$_SESSION['user_login_status'] = 1;
} else {
$this->errors[] = "Wrong password. Try again.";
$_SESSION['user_login_status'] = 0;
}
} else {
$this->errors[] = "This user does not exist.";
}
} else {
$this->errors[] = "Database connection problem.";
}
}
print_r($this->errors);
}
public function isUserLoggedIn()
{
if (isset($_SESSION['user_login_status']) AND $_SESSION['user_login_status'] == 1) {
return true;
}
// default return
return false;
}
}
?>
I run it in another file that is essentially the following:
//Run file
require_once("dbconfig.php");
include_once("login.php");
$login = new Login();
if($login->isUserLoggedIn() == true){
//go to another page }
The variables used to access the database are instantiated in dbconfig.php and are correct. With this code I get an error that says the page is not working and is unable to handle the request. When I comment out the line
if (!$this->db_connection()->connect_errno) {
and the else statement following it, the output is "This user does not exist". So I think the error has something to do with $this->db_connection()->connect_errno). If you can find where I went wrong or have any advice on how to rewrite the script to make it better, it is greatly appreciated.
This is a database establishing error your live remote server database configuration is different.Please verify you dbconfig.php file make sure
database name, host , port , username , password are well defined with your live database
This is wrong:
if (!$this->db_connection()->connect_errno) {
db_connection is simply a variable containing your DB connection object. It is NOT a method.
You probably want
if (!$this->db_connection->connect_errno) {
^--note lack of ()
instead.
I think issue with this follwoing check. your result gets more than 1 records.
// if this user exists
if ($result_of_login_check->num_rows == 1) {
......
}else{
$this->errors[] = "This user does not exist.";
}
make sure your email address is unique in Data table, if it is not unique then your above statement will fail and show the text "This user does not exist." from else part
I have recently dived into OOP & PHP MVC Application Design. At the moment I am learning a lot but I have one thing that is currently bugging me.
I read and now understand why it isn't wise to place http redirects within a service layer. We do not know what the controller will need to do once the service is complete, etc. etc. I also read that the service should not do anything outside of its purpose. Example: User Registration should only create a new user, using input passed by controller, but I am wondering if it is also fine to set flash messages within the service layer.
My application displays a lot of flash messages session based notifications for users. All of them are based on service related input validation checks, and produce alerts similar to the following
The username xxxxxx is already in use
Usernames Should be > 5 Characters
Should/can this be defined/set within the service class or is there something wrong with that? I have a Alert Helper function that handles setting the alerts. I can easily use my dependency injector to make it available I am just wondering if there is an issue with doing that.
I made the mistake of implementing all redirects within the services and I just finished removing all of them and placing them in the controllers, I don't want to make the same time consuming mistake so I am looking for advice here.
Thank you in advance for the help.
EDIT - CODE EXAMPLE
<?php
/**
*-----------------------------------------------------------------
*
* LOGIN CONTROLLER
*
*/
namespace Controller\www;
use \Helper\Controller;
class Login extends Controller {
public $dependencies = ['arena', 'login', 'site'];
/**
* Login
*
* Login Runs Through Various Checks Including If User is Banned, Account is Locked,
* or Forgot Password Request Is Active. Then the Entered Password is Matched & if Valid
* User is Logged In
*/
public function index() {
// Define Default
$username = '';
/**
* User Login
*
* If Successful, Login User, Redirect Home
* Else Set Error Alerts
*/
if ($this->form->post('login')) {
// Define and Sanitize Post Data
$username = $this->input->get('username');
$password = $this->input->get('password');
// Login Service Layer
$login = $this->factory->make('user/login');
// If Successful Redirect Home - Else Set Errors
if ($login->user($username, $password) === true) {
$this->redirect->home();
}
$this->alert->error($login->get('errors'));
}
/**
* Define Site Title & Display Page
*/
$this->view->sitetitle('login');
$this->view->display('www/login', [
'video' => $this->arena->video(),
'username' => $this->input->set($username)
], ['notifications' => 'user/forgotpassword']);
}
}
Service Layer
/**
*-----------------------------------------------------------------
*
* USER LOGIN SERVICE LAYER
*
*/
namespace Service\User;
use \Helper\Service;
class Login extends Service {
public $dependencies = ['login', 'mail', 'time', 'user', 'vars'];
/**
* Handles Entire Login Process For Site Users
*
* #params all User Submitted Form Data
*/
public function user($username = '', $password = '') {
// Validate $_POST Form Data
$this->validateInput($username, $password);
/**
* No Errors Produced - Complete Form Submission
*
* We Are Not Using `elseif` Between Forgot Password & Normal Login
* After a Forgot Password Code is Generated User May Remember Old Passwords
* We Need to Ensure Users Can Still Login Using Account Password As Well
*/
if (!$this->errors()) {
/**
* User Input Password Matches Account Password
*/
if ($this->input->verifyhash($password, $this->user->get('info.password'))) {
$this->login->user();
return true;
}
/**
* If We Have Not Been Redirected Login Was Unsuccessful
*/
$message = $forgotPW ? 'Forgot Password Code Invalid - Login Lost Incorrect' : 'Login Unsuccessful - Incorrect Username or Password';
$this->log->error($message, ['Username' => $username, 'Password' => $password]);
$this->error('Incorrect Username or Password');
}
/**
* If We Have Made It This Far Login Was Unsuccessful - Log Unsuccessful Attempt
*/
$this->login->logAttempt();
return false;
}
/**
* Validate $_POST Data
*
* #params all User Submitted Form Data
*/
private function validateInput($username = '', $password = '') {
// Display Error if Username is Empty
if (!$username) {
$this->error('Please enter a username');
}
// Display Error if Password is Empty
elseif (!$password) {
$this->error('Please enter a password');
}
// Search DB For User With Matching Username - If User Not Found Display/Log Error, Else Set User
else {
$user = $this->user->info($username, 'username', '', '`userid`');
if (!$user) {
$this->error('The username ' . $username . ' does not exist');
$this->log->error('User Not Found When Attempting to Login', ['username' => $username]);
} else {
$this->user->set('user', $user['userid']);
}
}
}
}
In order to answer your question, I think it's best to break down the concept of MVC into a very basic form, and its individual parts. I apologise in advance if this comes across as being somewhat condescending.
View
The view of the application displays anything and everything. If something is going to be displayed, it should be done in this layer
Controller
The controller is a mediator between the view and the model. It takes input from the view, applies logic/rules to it (where required), and interacts with the model to then get data to pass back to the view.
Model
This is where the loading and saving of data are done. The majority of the validation should have been done as part of the rules in the controller, and this should only pass details of any errors during loading or saving back to the controller should the arise. If there are no errors, it should return the relevant data, or a success status back to the controller.
With those points in mind, the model should not set flash messages to the session, that should be done within the controller depending on the result from the model.
Look at redirects and alerts as specific to one particular form of UI, and it should be obvious that there's no place for them in the Model. Simply always try to picture an alternative interface for your application; e.g. a command line interface for administrative tasks or a REST API. Redirects obviously have no place in either of these alternatives. Alerts are debatable... at the very least the form of the alert will be very different. Your Model will need to be able to pass back some status code to your Controller or View, and then it's the job of the Controller to react to "negative" events and the job of the View to visualise any alerts if necessary.
For example, your model may do something like this:
public function registerUser(User $user) {
...
if (!$successful) {
throw new EmailAlreadyRegisteredException;
}
return true;
}
The controller may then look like this:
public function userRegistration(Request $request) {
try {
$user = User::fromRequest($request);
$this->services->get('Users')->registerUser($user);
$this->view->render('registration_successful', $user);
} catch (InvalidUserData $e) {
$this->view->render('registration_form', $request, $e);
} catch (EmailAlreadyRegisteredException $e) {
$this->view->render('registration_failed', $user, $e);
}
}
The "alert" is passed around as an exception. It's just a method for the Model to signal to its callers what happened. It's up to the callers then to react to and visualise those events. You should certainly not expect any particular type of visualisation in the Model. So you don't want to hardcode specific HTML encoded messages or such. You don't even want to touch human languages at all, that's all the job of the View.
I am relatively new to OO PHP and I am trying to create a login class.
The issue I am having is that I want to pass the POST values username and password to my class but I cannot establish a decent way of doing so.
below is a snippet of my class
class PortalLogin{
private $username;
private $password;
function __construct(){
//I connect to DB here
}
function login($username, $password){
//error check the paramaters here
//then I can run the query
}
function __destruct(){
//I disconnect from DB here
}
}
Above is a breakdown of the class I am creating below is how i plan to execute it (my main issue at the moment).
$login = new PortalLogin();
if(isset($_POST['username'])){
if(isset($_POST['password'])){
$login->login($_POST[username],$_POST[password]);
} else {
//throw error
}
} else {
//throw error
}
I really do not like the construction of the code above it seems to messy to be doing so much outside of my class. how can I pass the POST information to the class and execute the checks there? I am worrying that if I pass the POST information to the class and one of the POSTS contains nothing it will error.
I think you got a problem with the syntax of post..
if(isset($_POST['username']) && isset($_POST['password'])){
$login->login($_POST['username'],$_POST['password']);
}
use AND.. so if both username and password exist then call the login function()
I’m not sure where OOP comes in to this, but if you were going the object-oriented route you would have a class that represents a request from which you could grab POST data from:
$username = $request->post('username');
$password = $request->post('password');
Your post() method could return a default value (null) if the variable didn’t exist in the POST data.
You could then have a class that checks your user based on these variables:
$auth = new AuthService($dbConnection);
if ($auth->checkCredentials($username, $password)) {
// Valid user
} else {
$error = $auth->getLastError();
}
I know I might be in the minority with suggesting this, but I favour static methods for things like this. PortalLogin represents an action rather than data
class PortalLogin
{
/**
* Attempt login
* #param string $username
* #param string $password
*/
public static function login ($username, $password)
{
// do your login stuff
}
}
Then to use you would do this:
if (isset($_POST['username']
&& !empty($_POST['username']
&& isset($_POST['password']
&& !empty($_POST['password']
) {
PortalLogin::login($_POST['username'], $_POST['password']);
}
Even better OO would be to have the username/password checking baked into the User class. (Maybe User::checkLoginCredentials($u, $p); // boolean yup/nope)
You can use error suppression, like this:
$login->login(#$_POST['username'], #$_POST['password']);
If one or both values are not present in the $_POST variable, there won't be an error when calling the method, so you can do the error handling inside your class method.
For more info, check:
http://php.net/manual/en/language.operators.errorcontrol.php
Edit:
Another option is to do this:
$login->login((isset($_POST['username']) ? $_POST['username'] : null), (isset($_POST['password']) ? $_POST['password'] : null));
I have a class in PHP called cUser:
class cUser {
var $m_email;//The users email adresse(String)
var $m_password;//His password(String)
var $m_username;//His username(String)
var $m_active;//If the user have been activate (By following a link send to him via is email)(Bool)
function __construct($p_username, $p_password, $p_email, $p_active) {
$this->m_username = $p_username;
$this->m_password = $p_password;
$this->m_email = $p_email;
$this->m_active = $p_active;
}
//this is the important part...
function connexion() {
include "Config.php";//include all the parameters needed to connect to the DB
$cn = new cConnexion($ConnexionDBHost, $ConnexionDBName, $ConnexionDBLogin, $ConnexionDBPassword);//Initiate a connection to the DB
if($cn->DBConnexion())//If it is connected {
$parameters = array('username'=>$this->getUsername(), 'password'=>$this->getPassword());//create an array with the username and the password
$getConnexion = $cn->SecureSelect("SELECT username, password, email, active FROM user WHERE BINARY username = :username AND BINARY password = :password", $parameters);//selecte the user in the DB (for DB description see below code)
if($getConnexion != null) { //if there is no error in the query.
$resultSet = $getConnexion->fetch();//fetch the results
if($resultSet != null) { //if there is a match
//assigne the DB field values to this instance of cUser
$this->setUsername($resultSet['username']);
$this->setPassword($resultSet['password']);
$this->setEmail($resultSet['email']);
$this->setActive($resultSet['active']);
if($this->getActive() == 1) {
//If the user has been activate already return success
}
else {
//Else send an activation email to the user.Dont connecte him and return an error message
}
}
else {
//Send an error message
}
}
else {
//send an error message
}
}
else {
//send an error message
}
}
//this are not important for the question but I put them there so you can see what kind of operation the class is doing.
function delete(){//Delete this instance of cUser from de DB}
function insert(){//Insert this instance of cUser from the DB}
function update($p_email, $p_username, ...){//Update this instance of cUser with the new parameters}
function activateAccount(){//Activate this instance of cUser}
//And all the getters and setters associate with the class attributes.
}
Here is the MySQL Table containing the field for the cUser class (roughly coded):
USER
varchar email,
varchar password,
varchar username,
tiny int activate,//1 or 0
tiny int connected//1 or 0
Question:
How can I implement or change the function connection so one instance of a user is connected at the same time?
Note:
I already know I can just check if the DB connected field is set to 1 but if two user access the DB at the same time it would create a problem (race condition or something like that).
Is there something like a mutex or semaphore I can use to sync the access of the DB field connected??
Example:
David fill the HTML form and submit it with user name and password ("Dav1", "ThisIsPassword"), a process page create the cUser instance and connect to check if Dav1 already existe then give him access to the rest of the web-app.
Now Davos fill the form and submits it with the same user-name and password that David used because Davos and David are friend and they shared the same account and shared there password.
With the existing code both David and Davos can then access the web application at the same time with the same account what I want is that when David connect Davos get an error message that tell him that the user is either already connected or the user-name/password doesn't match.
Use a transaction.
In MySql you can also use SELECT FOR UPDATE statement.
Pseudocode:
$transaction = db->beginTransaction();
try {
$user = User::getByUsername($username);
if ($passwordImcorrect)
throw new Exception('invalid credentials');
if (user->loggedIn)
throw new Exception('already logged in');
user->loggedIn = 1;
user->save();
$transaction->commit();
}
catch (Exception $e) {
echo $e->getMessage();
$transaction->rollback();
}