I have a php file(register.php) with a public function register($data) where errors are validated.Then errors are counted and if no errors are found, validation is passed.
register.php:
class ARegister {
public function register($data) {
$user = $data['userData'];
//validate provided data
$errors = $this->validateUser($data);
if(count($errors) == 0) {
//first validation
}
}
public function validateUser($data, $botProtection = true) {
$id = $data['fieldId'];
$user = $data['userData'];
$errors = array();
$validator = new AValidator();
if( $validator->isEmpty($user['password']) )
$errors[] = array(
"id" => $id['password'],
"msg" => Lang::get('password_required')
);
return $errors;
}
The problem is, that I need to get this confirmation of validated data to my other php file (othervalidation.php) where I've made another validation:
othervalidation.php:
<?php
require 'register.php';
if ( !empty($action) ) {
switch ( $action ) {
case 'process_payment':
try {
$instance = new ARegister();
if($instance->validateUser($data, $errors)) {
throw new Exception('Validation error');
}
} catch (Exception $e) {
$status = false;
$message = $e->getMessage();
}
}
How can I send the result of $errors variable to my other validation (othervalidation.php)?
I looked at your new code design and here's the new problems I found.
First, in your register function, you use the errors variable as an integer while your validate function returns an array. You got two possibilities here.
You can change your register method to check out if your error array is empty like this:
if(empty($errors)) {
//first validation
}
Count is also valid, but I still prefer empty since it's syntactically clearer. Furthermore, the count function returns 1 if the parameter is not an array or a countable object or 0 if the parameter is NULL. As I said, it is a functional solution in your current case but, in some other contexts, it might cause you unexpected results.
Here in your method declaration, I see that you are expecting a boolean (botProtection).
public function validateUser($data, $botProtection = true) {
But you are supplying an errors parameter
if($instance->validateUser($data, $errors)) {
You don't provide me the declaration of the errors variable, but it is probably not matching the bot protection parameter your function is expecting. PHP is using lose typing, it is useful but, once again, you got to be careful for bugs hard to find. For public function, you should always make sure a way or another that the supplied parameter won't lead to code crash.
In your code, the data parameter seems to be an array. You can use parameter hinting to force the use of array like this:
public function register(array $data) {
public function validateUser(array $data, $botProtection = true) {
And even specific class (as if you where using "instance of" in a condition)
public function register(MyDataClass $data) {
public function validateUser(MyDataClass $data, $botProtection = true) {
Also, you're not even using the botProtection parameter in your validateUser method.
On the same function call:
if($instance->validateUser($data, $errors)) {
you are expecting a Boolean (true or false), but the method returns an array. If you want to use the code the way it is currently designed, you must use it like this
if(!empty($instance->validateUser($data, $errors)) {
Here, I'm not so sure it is necessary to use exception. Ain't it be easier to design your code like this?
if(!empty($instance->validateUser($data, $errors)) {
$message = 'Validation error';
}
In your validate function, is the "isEmpty" function also validating if the client provided a password?
If that's the case you could validate it like this:
if(!in_array($user['password']) or empty($user['password']))
With those corrections, your code should be functional.
Here's a sample of how I would had design your code (considering the code sample provided):
class ARegister {
public function register($data) {
$user = $data['userData']; //don't declare it here, all the user validations must be done in validateUser($data, &$errors)
$errors = array();
if($this->validateUser($data, $errors)) {
//first validation
}
}
/**
* Note: If you are not returing more than one error at the time, $errors should be a string instead of an array.
*/
public function validateUser($data, array &$errors) {
$isValid = false;
if (in_array($data['fieldId']) and in_array($data['fieldId']['password']) and in_array($data['userData'])){
if(!in_array($data['userData']['password']) or empty($data['userData']['password'])){
$errors[$data['fieldId']['password']] = Lang::get('password_required');
}
else{
$isValid = true;
}
}
else{
//an invalid data array had been provided
}
return $isValid;
}
For the next part, if the code is executed directly in the view and you are a beginner, create a procedural external controller file (all functions will be public...). If you are a professional, you MUST create a class to encapsulate the treatment.
You must not do treatment directly in the view. The view is a dumb placeholder for data presentation and collecting client's input. The sole action it must do is display the data sent by the controller and send back the client's input to the controller.
The treatment on data is the controller responsibility.
if (!empty($action) ) {
$errors =array();
switch ( $action ) {
case 'process_payment':
$instance = new ARegister();
if($instance->validateUser($data, $errors)) {
//the user is valid, do the treatment
}
else
PageManager::dispayError($errors);
}
unset($instance);
}
}
Here's an example how you can centralize your error display
/**
* Can be more complexe than that, but I'm at my father's home at four hundred kms away from Montreal right now..
*/
public static function dispayError($errors, $size = 4){
if (is_numeric($size)){
if ($size < 0){
$size = 1;
}
elseif($size > 5){
$size = 5;
}
}
else{
$size = 4;
}
if (is_scalar($errors)){
echo '<h' . $size . 'class="ERROR_MESSAGE">' . $errors . '</h' . $size . '><br>';
}
elseif (is_array($errors)){
foreach ($errors as $error){
if (is_scalar($error)){
echo '<h' . $size . 'class="ERROR_MESSAGE">' . $error . '</h' . $size . '><br>';
}
}
}
}
Of course, you can also support many kind of message:
public static function dispayError($errors, $size = 4){
self::displayMessage("ERROR_MESSAGE", $errors, $size=4);
}
private static displayMessage($class, $messages, $size=4)
Well, took me two hours to write that. I hope you have now enough material to build an efficient, reusable and, no less important, safe code design.
Good success,
Jonathan Parent-Lévesque from Montreal
You can try something like this:
class ARegister {
private $error = 0;
public function register($data) {
if (!$this->validateUser($data)){
$this->error++;
}
}
public function getErrorCount(){
return $this->error;
}
public resetErrorCount(){
$this->error = 0;
}
Or pass the error by reference:
public function register(&$error, $data) {
if (!$this->validateUser($data)){
$error++;
}
}
Personally, I would do all the validation in the same method (in the class for encapsulation), use an error message parameter (passed by reference) to return why the validation failed and use the return statement to return true or false.
class MyClass{
public function validation(&$errorMessage, $firstParameter, $secondParameter){
$success = false;
if (!$this->firstValidation($firstParameter)){
$errorMessage = "this is not working pal.";
}
elseif (!this->secondeValidation($firstParameter)){
$errorMessage = "Still not working buddy...";
}
else{
$success = true;
}
return $success;
}
private function firstValidation($firstParameter){
$success = false;
return $success;
}
private function secondeValidation($secondParameter){
$success = false;
return $success;
}
}
In your other file:
<?php
$instance = new MyClass();
$errorMessage = "";
if ($instance->validation($errorMessage, $firstParameter, $secondParameter)){
echo "Woot, it's working!!!";
}
else{
echo $errorMessage;
}
?>
Is one of these code solutions fit your needs?
Jonathan Parent-Lévesque from Montreal
Related
I am developing a Register/Login system with validation. Registering system is working well. For example, when I register the same email twice, the following message appears:
Email already registered!
However, when I log-in with the same e-mail and password, an error occurs. The following message appears as a validation error:
Email not registered!
Even if the email is registered in DB.
Code for e-mail validation:
<?php
public function validateEmail($par)
{
if (filter_var($par, FILTER_VALIDATE_EMAIL)) {
return true;
} else {
$this->setErro("Invalid Email!");
return false;
}
}
public function validateIssetEmail($email, $action = null)
{
$b = $this->cadastro->getIssetEmail($email);
if ($action == null) {
if ($b > 0) {
$this->setErro("Email already registered!");
return false;
} else {
return true;
}
} else {
if ($b > 0) {
return true;
} else {
$this->setErro("Email not registered!");
return false;
}
}
}
Code for login controller:
<?php
$validate = new Classes\ClassValidate();
$validate->validateFields($_POST);
$validate->validateEmail($email);
$validate->validateIssetEmail($email,"login");
$validate->validateStrongSenha($senha);
$validate->validateSenha($email,$senha);
var_dump($validate->getErro());
Code for class login:
<?php
namespace Models;
class ClassLogin extends ClassCrud
{
# Returns user data
public function getDataUser($email)
{
$b = $this->selectDB(
"*",
"users",
"where email=?",
array(
$email
)
);
$f = $b->fetch(\PDO::FETCH_ASSOC);
$r = $b->rowCount();
return $arrData = [
"data" => $f,
"rows" => $r
];
}
}
My getIssetEmail method exists on Register code only.
# Check directly at the bank if the email is registered
public function getIssetEmail($email)
{
$b = $this->selectDB(
"*",
"users",
"where email=?",
[
$email
]
);
return $r = $b->rowCount(); // returns the amount of rows in the search
}
And ClassPassword
<?php
namespace Classes;
use Models\ClassLogin;
class ClassPassword
{
private $db;
public function __construct()
{
$this->db = new ClassLogin();
}
# Create password's hash to save in DB
public function passwordHash($senha)
{
return password_hash($senha, PASSWORD_DEFAULT);
}
# Verify if password's hash is correct
public function verifyHash($email, $senha)
{
$hashDb = $this->db->getDataUser($email);
return password_verify($senha, $hashDb["data"]["senha"]);
}
}
This is not an answer but hopefully it will help in debugging.
First, I'm going to change your code. This is 100% a style choice but I personally think it is easier to follow. If you have an if statement that always returns, you don't technically need an else. Once again, this is a style choice and you don't have to follow it.
Second, if you can, try adding logging into your workflow, it will save you so much time debugging. It isn't always an option, especially for legacy code bases, but it is awesome when you can inspect complex code. In this example, I"m just making a couple of helper methods that dump stuff but normally I'd use something like Monolog to write to a stream that I can tail, and I can easily turn it off in production. When logging, sometimes it helps to avoid identical messages so that you can easily find the exact line number you are on, too.
So with those changes, try running this code inside of your class:
private function logMessage($message)
{
echo $message . PHP_EOL;
}
private function logVariable($variable)
{
var_dump($variable);
}
public function validateIssetEmail($email, $action = null)
{
$this->logVariable($email);
$this->logVariable($action);
$b = $this->cadastro->getIssetEmail($email);
$this->logVariable($b);
if ($action === null) {
$this->logMessage('Action was null');
if ($b > 0) {
$this->logMessage('B is greater than zero');
$this->setErro("Email already registered!");
return false;
}
$this->logMessage('B was not greater than zero');
return true;
}
$this->logMessage('Action was not null');
if ($b > 0) {
$this->logMessage('B is greater than zero');
return true;
}
$this->logMessage('B was not greater than zero');
$this->setErro("Email not registered!");
return false;
}
This should log in human-readable form every step. You should be able to walk through this and identify where your bug is. For instance, in the comments above you said that a variable was 0 in a block that was guarded by a check that guarantees that that shouldn't happen.
This is the wrong part i guess you assigned login as action so you can call cadastro class inside of the function
$cadastro = new Cadastro();
$b = $cadastro->getIssetEmail($email);
if ($action == null) {
if ($b > 0) {
$this->setErro("Email already registered!");
return false;
} else {
return true;
}
} else {
if ($b > 0) {
return true;
} else {
$this->setErro("Email not registered!");
return false;
}
}
I am writing a method that uses POST variables posted by AJAX to add a user to a certain course in the database, but I can't get the callback to work correctly:
public function enroll()
{
$package = array();
$this->load->library('form_validation');
$this->form_validation->set_rules('course', 'Vak', 'required|callback_not_enrolled');
$fields = array("course");
if ($this->form_validation->run($this) === FALSE) {
$errors = array();
$success = array();
foreach ($fields as $field) {
$error = form_error($field);
if ($error !== "") {
$errors[$field] = $error;
} else {
$success[$field] = True;
}
}
$package["field_errors"] = $errors;
$package["field_success"] = $success;
$package["success"] = False;
} else {
$package["database"] = $this->course_model->enroll_user($this->data["user"], $this->input->post("course"));
$package["success"] = True;
}
echo json_encode($package);
}
I wrote the callback not_enrolled to check if the user is not already enrolled to the database. Note that I can't use is_unique because I have to test the combined uniqueness of two fields (so just one or two separate ones don't do the trick) and the id of the user is not included in the form (because it's part of the Code Igniter session).
The callback function:
public function _not_enrolled($course)
{
$exists = ($this->user->is_enrolled($course, $this->data["user_id"]) != False);
if ($exists != False) {
$this->form_validation->set_message("not_enrolled", "Already enrolled");
return False;
} else {
return True;
}
}
And finally the method is_enrolled from the model:
public function is_enrolled($course, $user=False) {
if($user==False){
$user = $this->data["user_id"];
}
$this->db->select()->from("course_participant")->where("user_id", $user)->where("course_id", $course);
$query = $this->db->get();
return($query->num_rows()>0);
}
Through a call to var_dump($this->_not_enrolled($existing_course_id)); I know that both the callback function and the method from the model work, as it correctly returned true.
When I var_dump the $package array or validation_errors() I don't get any validation errors except that it says Unable to access an error message corresponding to your field name Vak(not_enrolled).
I tried removing the initial _ from the function name but that gives me a Server Status 500 error.
I have another setup exactly like this, albeit other database calls, with a callback using the same syntax. This method works perfectly.
I have a quick question that's killing my head.
I'm trying to make a Form Validation System with Method Chaining in PHP
What I want to do is to be able to call for example (please check the code comments):
$firstname = $OBJECT->Forms->Field("First Name", "firstname"); //This one doesn't validate, but just puts what's on firstname field on to $firstname. But this way doesn't work for me, because I have to return the object so it can be chainable and not the variable of the POST. How can I do this?
$firstname = $OBJECT->Forms->Field("First Name", "firstname")->Validate(); //this one validates if the field is not empty and if it's empty it'll insert the first parameter ("First Name") onto an array to display the errors.
$email = $OBJECT->Forms->Field("Email", "email")->Validate()->Email(); //This one does the same as above but validates Email and inserts the value of the email field onto $email
but I prefer the next one...
$email = $OBJECT->Forms->Field("Email", "email")->Validate->Email(); //I'd rather prefer this method but I don't know how to do it without using the parenthesis on the Validate method.
I can only make it work like this
$firstname = $OBJECT->Forms->Field("First Name", "firstname")->Validate();
and
$firstname = $OBJECT->Forms->Field("First Name", "firstname")->Validate()->Email();
Without ->Validate(); I can't seem to make it work (Like this: $firstname = $OBJECT->Forms->Field("First Name", "firstname");)
The code is kinda mess to share. But the code is simple... I have a forms.class.php and a validate.class.php.
The forms.class.php creates an instance of Validate class from validate.class.php and the Forms Object is passed through the Validate class on the constructor.
I want to be able to do:
$OBJECT->Forms->Field();
$OBJECT->Forms->Field()->Validate();
$OBJECT->Forms->Field()->Validate()->Email;
$OBJECT->Forms->Field()->Validate()->Telephone;
or this preferebly:
$OBJECT->Forms->Field();
$OBJECT->Forms->Field()->Validate;
$OBJECT->Forms->Field()->Validate->Email;
$OBJECT->Forms->Field()->Validate->Telephone;
Only figured out:
$OBJECT->Forms->Field()->Validate();
$OBJECT->Forms->Field()->Validate()->Email();
$OBJECT->Forms->Field()->Validate()->Telephone();
But any form is OK
Thank you.
See if this is what you are trying to do:
<?php
class FormValidate
{
protected $args;
public $valid;
public function Forms()
{
// Don't know what this function is supposed to do....
return $this;
}
public function Validate()
{
$numargs = func_num_args();
$this->args = array();
if($numargs == 2) {
$vals = func_get_args();
$this->args[$vals[1]] = $vals[0];
$this->valid = true;
}
else
$this->valid = false;
if(isset($this->args['firstname']) && !empty($this->args['firstname']))
return true;
return $this;
}
public function Email()
{
if(isset($this->args['email'])) {
if(filter_var($this->args['email'],FILTER_VALIDATE_EMAIL))
return $this->valid = $this->args['email'];
}
return $this->valid = false;
}
public function Telephone()
{
if(isset($this->args['telephone'])) {
if(preg_match('/^[0-9]{3}-[0-9]{3}-[0-9]{4}$/',$this->args['telephone']))
return $this->valid = $this->args['telephone'];
}
return $this->valid = false;
}
}
$test = new FormValidate();
// These will throw a fatal error on the base Validate('First Name','firstname')
// if you add another method to the chain like so: ->Validate('First Name','firstname')->Email();
echo $test->Forms()->Validate('123-876-0987','telephone')->Telephone();
?>
I have a save method in my User class.
If the save method encounters validation errors it returns an array of errors that I display to the user. However this means in my code I have to write:
if (!$user->save()) {
//display success to user
}
Surely my save method should return true on success. But how do I handle errors in that case?
Use try ... catch syntax.
For example:
try {
$user->save();
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
}
http://php.net/manual/en/language.exceptions.php
I would throw an exception in the event that save() runs into any problems.
If you want to provide an array of validation errors, your could subclass Exception and provide a mechanism for storing the validation errors.
A custom Exception subclass will also help you differentiate between exceptions your code throws explicitly (which you'd like to catch) and exceptions that you didn't expect (which should be fatal).
Here's the subclass:
class UserException extends Exception
{
private $userMessages;
public function __construct($message = "", $code = 0, Exception $previous = null, array $userMessages = null)
{
parent::__construct($message, $code, $previous);
if ($userMessages === null) {
$this->userMessages = array();
} else {
$this->userMessages = $userMessages;
}
}
public function getUserMessages()
{
return $this->userMessages;
}
}
Here's a silly version of a User class that always throws an Exception on save().
class User
{
public function save()
{
$userMessages = array(
'Your password is wrong',
'Your username is silly',
'Your favorite color is ugly'
);
throw new UserException('User Errors', 0 , null, $userMessages);
}
}
To use it:
$user = new User();
try {
$user->save();
} catch (UserException $e) {
foreach ($e->getUserMessages() as $message) {
print $message . "\n";
}
}
You could also accomplish something like this by populating the Exception's $message with, say a semi-colon-delimited list of messages. You could even build a list of constants for error types, then combine them as a bitmask and use that for the Exception's $code. The advantage of these options is you would be using the built in members and not adding anything extra.
More on exceptions:
http://php.net/manual/en/language.exceptions.php
A (bad?) habit I picked up after playing a good bit with erlang is to return tuple values (as a php array).
function my_func() {
$success = true;
$errors = array();
if ( something_fails() ) {
$success = false;
$errors[] = 'something failed..';
}
return array( $success, $errors );
}
list($success, $errors) = my_func();
if ( ! $success ) {
do_somthing_with( $errors );
}
In my experience, this has been really handy when the wild modify legacy code tickets appear and you don't really dare modify anything but could more easily add more legacy to it.
Cheers -
Return either true, or the error array.
And when you check for it, use this:
if ($user->save()===true) {
// display success to user
} else {
// display error to user
}
the === operator performs a typesafe comparison, meaning that it not only checks if the value is true, but also if the type is a boolean. If the array is being returned it's handled as false.
Would be good to return array from validation function like this
$result['ACK'] = 'true';
$result['message'] = 'Success validation'];
on failure
$result['ACK'] = 'false';
$result['message'] = 'validation error message';
Now you can use this array in front end like this
if ($result['ACK']) {
//No Error
} else {
echo $result['message'];
}
Change your condition to, If true then success else return array of errors.
if ($user->save() === true) {
//display success to user
}
Inherited an old CakePHP site and I'm trying to figure out what some functions do. I have several functions that have the same name as another function but with an underscore first, e.g. save() and _save(). However the function _save() is never called in any context, though save() is.
I read this question and it looks like it's from an old worst-practices exercise, but that doesn't really explain why it's in my code; you still have to call function _save() as _save() right? If there's no calls to _save() is it safe to remove?
I want it gone, even the save() function wasn't supposed to be there, rewriting perfectly good framework functionality. It looks like an older version of the same function, but there's no comments and I don't know if there's some weird context in which php/Cake will fall back to the underscored function name.
Here's the code for the curious. On closer inspection it appears the underscored functions were old versions of a function left in for some reason. At least one was a "private" method being called (from a public function of the same name, minus the underscore...):
function __save() {
$user = $this->redirectWithoutPermission('product.manage','/',true);
if ($this->data) {
$this->Prod->data = $this->data;
$saved_okay = false;
if ($this->Prod->validates()) {
if ($this->Prod->save()) $saved_okay = true;
}
if ($saved_okay) {
$product_id = ($this->data['Prod']['id']) ? $this->data['Prod']['id'] : $this->Prod->getLastInsertId();
if ($this->data['Plant']['id']) {
$this->data['Prod']['id'] = $product_id;
$this->Prod->data = $this->data;
$this->Prod->save_plants();
$this->redirect('/plant/products/'.$this->data['Plant']['id']);
} else {
$this->redirect('/product/view/'.$product_id);
}
die();
} else {
die('did not save properly');
}
} else {
die('whoops');
}
}
function save() {
$user = $this->redirectWithoutPermission('product.manage','/products',true);
if ($this->data) {
$this->Prod->data = $this->data;
if ($this->Prod->validates()) {
$this->Prod->save();
$gotoURL = isset($this->data['Navigation']['goto'])?$this->data['Navigation']['goto']:'/';
$gotoURL = str_replace('%%Prod.id%%', $this->data['Prod']['id'], $gotoURL);
if (isset($this->data['Navigation']['flash'])) {
$this->Session->setFlash($this->data['Navigation']['flash']);
}
if (isset($this->params['url']['ext']) && $this->params['url']['ext']=='ajax') {
$value = array(
'success'=>true
,'redirect'=>$gotoURL
);
print $this->Json->encode($value);
} else {
$this->redirect($gotoURL);
}
} else {
$value = array(
'success'=>false
,'message'=>"You have invalid fields."
,'reason'=>'invalid_fields'
,'fields'=>array(
'Prod'=>$this->Prod->invalidFields()
)
);
print $this->Json->encode($value);
}
} else {
$this->redirect('/products');
}
die();
}
I had hoped to learn whether or not some convention applied to this situation, but from testing I've found the functions are not called which is really the answer to the question I asked.