A better way than multiple if condition in php? Is too many if condition there, so i need something more simple and efficient...and you can show me examples?!
if($_GET['id']=="4") {
try {
$ids = '4,246,226';
$notin = '1';
$parent_forum = 'php_forums.parent_id';
} catch(PDOException $e) {
echo 'ERROR: ' . $e->getMessage();
}
}
if($_GET['id']=="246") {
try {
$ids = '246';
$notin = '1';
$parent_forum = 'php_forums.parent_id';
} catch(PDOException $e) {
echo 'ERROR: ' . $e->getMessage();
}
}
if($_GET['id']=="226") {
try {
$ids = '226';
$notin = '1';
$parent_forum = 'php_forums.parent_id';
} catch(PDOException $e) {
echo 'ERROR: ' . $e->getMessage();
}
}
The thing about code is that you can tackle one problem in many different ways. It is all justified depending on the environment and how you want to organise your problem and the dimension of your project.
So, for example, as mentioned in the comments, a good replacement for if conditions are switch cases.
Switch cases example
I am not aware of the entire structure behind your question, but I will provide a solution on how I would do it.
/**
* #throws Exception
*/
public function doSomethingWithGET()
{
$notin = 1;
$parent_forum = 'php_forums.parent_id';
switch ($_GET['id']) {
case '4':
case '246':
case '226':
$ids = $_GET['id'];
break;
default:
throw new Exception('Specify your error message here');
}
}
The only reason why i grouped the switch cases is because nothing seemed to change in your code that would justify clear separation, BUT to make you aware you can both separate the cases to be very specific depending on your IDs OR group certain IDS if only a few behave equally, while separating specific IDS for very specific behaviours.
Method calls
This scenario is just a different way to tackle the same problem. Just for giggles and playfulness of it :) I am sure it has its own play cases, just like get_class can avoid us to constantly create a switch case for factory classes, but as mentioned before, it all depends on your idea :)
class Test
{
/**
* #var array $idsAndActions
**/
protect $idsAndActions;
public function __construct ()
{
$this->idsAndActions = [
'4' => 'doSomethingWithGET',
'246' => 'doSomethingWithGET',
'226' => 'somethingDifferent',
];
}
public function parseGet ()
{
if (array_key_exists($_GET['id'], $this->idsAndActions)) {
$methodCall = $this->idsAndActions[$_GET['id']];
if (method_exists($this, $methodCall)) {
$this->$methodCall();
//add here more code for success events
}
}
echo '[Error] Your message';
//You can also throw an exception, which I would advice depending on who calls this :)
}
public function doSomethingWithGET ()
{
$this->notin = 1;
$this->parent_forum = 'php_forums.parent_id';
$this->ids = $_GET['id'];
}
public function somethingDifferent ()
{
$this->notin = 20;
$this->parent_forum = 'another_thing_here';
$this->ids = $_GET['id'];
}
}
Hope it helps you giving some ideas on how you can tackle this or find a solution that best suits you! :)
NOTE: I know it is not mentioned, BUT if you are using the $_GET['id'] do do some kind of access to DBs or execute system commands do not forget the possibility of system breach and sql injection. Never trust variables external to the system
Related
Until yesterday I was burning my brain trying to switch from a procedural thinking to a OOP thinking; this morning I gave up. I said to my self I wasn't probably ready yet to understand it.
I started then coding in the usual way, writing a function to check if there's the cookie "logged" or not
function chkCookieLogin() {
if(isset($_COOKIE["logged"])) {
$logged = 'true';
$cookieValue = $_COOKIE["logged"];
return $logged;
return $cookieValue;
}
else {
$logged = 'false';
return $logged;
}
}
$result = chkCookieLogin();
if($result == 'true'){
echo $cookieValue;
}
else {
echo 'NO COOKIE';
}
since I run across a problem: I wanted to return two variables ($logged and $cookieValue) instead of just one. I google it and I found this answer where Jasper explains a method using an OOP point of view (or this is what I can see).
That answer opened me a new vision on the OOP so I tried to rewrite what I was trying to achieve this way:
class chkCookie {
public $logged;
public $cookieValue;
public function __construct($logged, $cookieValue) {
$this->logged = $logged;
$this->cookieValue = $cookieValue;
}
function chkCookieLogin() {
$out = new chkCookie();
if(isset($_COOKIE["logged"])) {
$out->logged = 'true';
$out->cookieValue = $_COOKIE["logged"];
return $out;
}
else {
$out->logged = 'false';
return $out;
}
}
}
$vars = chkCookieLogin();
$logged = $vars->logged;
$cookieValue = $vars->cookieValue;
echo $logged; echo $cookieValue;
Obviously it didn't work at the first attempt...and neither at the second and the third. But for the first time I feel I'm at one step to "really touch" the OOP (or this is what I think!).
My questions are:
is this attempt correctly written from the OOP point of view?
If yes, what are the problems? ('cause I guess there's more than one)
Thank you so much!
Credit to #NiettheDarkAbsol for the idea of returning an Array data-type.
Using dependency injection, you can set-up an object like this:
class Factory {
private $Data = [];
public function set($index, $data) {
$this->Data[$index] = $data;
}
public function get($index) {
return $this->Data[$index];
}
}
Then to use the DI module, you can set methods like so (using anonymous functions):
$f = new Factory();
$f->set('Cookies', $_SESSION);
$f->set('Check-Cookie', function() use ($f) {
return $f->get('Cookies')['logged'] ? [true, $f->get('Cookies')['logged']] : [false, null];
});
Using error checks, we can then call the method when and as we need it:
$cookieArr = is_callable($f->get('Check-Cookie')) ? call_user_func($f->get('Check-Cookie')) : [];
echo $cookieArr[0] ? $cookieArr[1] : 'Logged is not set';
I'd also consider adding constants to your DI class, allowing more dynamic approaches rather than doing error checks each time. IE, on set() include a constant like Factory::FUNC_ARRAY so your get() method can return the closure already executed.
You can look into using ternary operators if you're confused.
See it working over at 3v4l.org.
If it means anything, here is an OOP styled approach.
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
So here is my case:
I have three functions performing some chemical reactions(synthesis1(), synthesis2() & synthesis3() ).
All of these functions will give an answer or a fail in the results.
They were originally separate scripts but are now in a class.
NB: the functions work fine by themselves, even in the class.
Below is my script to instantiate the class and start the functions.
My problem is that since i am running a reaction which fires all the functions;
i get one 1 correct answer and two fails or three fail at once.
What is the best way to handle the situation.
I want one correct answer and suppress the two fails or just show one fail in case of three fails(all fails). I don't expect three right answers.
P.s. All answers are strings.
<?php
// create an object for class name
$aaa = new synthesis();
$abc = new synthesis();
$abcd = new synthesis();
// call the functions in the class
$synthesis1 = $aaa->synthesis1();
$synthesis2 = $abc->synthesis2();
$synthesis3 = $abcd->synthesis3();
// call the if functions
$searches = array($synthesis1, $synthesis2, $synthesis3);
foreach($searches as $search) {
if ($aaa->synthesis1($search)){
echo 'Match found: ' . $search;
break;
}
elseif ($abc->synthesis2($search)){
echo 'Match found: ' . $search;
break;
}
elseif ($abcd->synthesis3($search)){
echo 'Match found: ' . $search;
break;
}
else{ echo"Please try again or try another reaction";}
}
?>
I don't know why you need to instantiate three different objects if you have three individually named methods.
I would think you might want to add a method to your class to simply run all synthesis methods all at once and return the result. So something like:
class synthesis {
protected $synthesis_methods = array(
'synthesis1',
'synthesis2',
'synthesis3',
// add more methods here if needed
}
public function synthesis1() {
// your method logic here
}
public function synthesis2() {
// your method logic here
}
public function synthesis2() {
// your method logic here
}
public function synthesize_all() {
$result = false;
$i = 0;
while(false === $result && $i < count($this->synthesis_methods)) {
$result = call_user_func(array($this, $this->synthesis_methods[$i]));
$i++;
}
return $result;
}
}
You would then only instantiate a single object. Usage would be:
$synth_obj = new synthesis();
var_dump($synth_obj->synthesize_all());
An easy way to handle this is to use OR logic:
if($aaa->synthesis1($search) or $abc->synthesis2($search) or $abcd->synthesis3($search))
{
echo "Match Found: $search";
break;
}
else
{
echo "Please try again or try another reaction.";
}
Lately, I have been getting some strange duplicate entries in my MySQL database. Entries in this table are inserted during a PUT request to a PHP page. The table contains 3 fields with no external references:
manga_id (primary key; auto increment) - bigint(20);
name - varchar(255);
manga_cid - varchar(255).
The PHP code is the following:
class MangaHandler {
private function getMangaFromName($name) {
$id = $this->generateId($name);
$mangas = new Query("SELECT * FROM tbl_manga WHERE manga_cid = '" . $this->conn->escapeString($id) . "'", $this->conn);
if(!$mangas || $mangas->hasError()) {
logError("getMangaFromName($name): " . $this->conn->getError());
return null;
}
if($mangas->moveNext()) {
return $mangas->getRow();
}
return null;
}
private function addManga($name) {
$manga_row = null;
$error = false;
$cid = $this->generateId($name);
$sql = sprintf("INSERT INTO tbl_manga(name, manga_cid) VALUES ('%s', '%s')", $this->conn->escapeString($name), $this->conn->escapeString($cid));
if(!$this->conn->execute($sql))
$error = true;
// some more code ...
if($error) {
logError("addManga($name): " . $this->conn->getError());
}
return $manga_row;
}
public function addMangaSourceAndFollow($name, $url, $source_id, $user_id, $stick_source = false, $stick_lang = 'English') {
// validate url
$manga = $this->getMangaFromUrl($url, $source_id);
if(!$manga) {
$manga = $this->getMangaFromName($name);
if(!$manga) $manga = $this->addManga($name);
// ...
}
// ...
return true;
}
}
class MangaRestService extends CommonRestService
{
public function performPut($url, $arguments, $accept, $raw) {
header('Content-type: application/json');
header("Cache-Control: no-cache, must-revalidate");
$json = json_decode($raw, true);
// some input validation and auth
$ms = new MangaHandler();
try {
$ret = $ms->addMangaSourceAndFollow(null, $json['url'], $source['source_id'], $user['user_id'], $enforce == 1);
// throw exception if some ret is invalid
// return proper json response
} catch(Exception $e) {
$conn->rollback();
logError("MangaRestService.performPut($url, [" . implode("; ", $arguments) . "], $accept, $raw): " . $e->getMessage());
echo RestResponse::getSomeErrorResponse()->toJSON();
}
}
}
$serv = new MangaRestService();
// performs some checks and invokes the appropriate GET, POST, PUT or DELETE method
// in this case it's the performPut method above
$serv->handleRawRequest();
The manga name gets filtered (only alphanumeric chars, underscore and some more characters are allowed) and becomes the manga_cid (which should be unique in the table).
The code basically checks if a manga with a specific manga_cid exists. Only if it doesn't, a new entry is inserted. I have tested the code and it works, however, after deploying the app., I have been getting entries with duplicate manga_cid (sometimes more than 2). This is a rare occurrence, as most entries remain unique. Also, I have no errors in my logs.
Could it be that for some reason, multiple HTTP PUT requests are being executed concurrently and, since there is no synchronization, INSERT gets called multiple times?
I find it very unlikely, specially because the app only lets you press the button that performs this request once before it disappears.
I have tried using MySQL transactions but it didn't solve the problem. I know that setting the field as unique will probably allow me to avoid these duplicate entries, however I would have to perform some heavy maintenance on the database in order to remove the duplicate entries first. Although this table has a simple structure, the manga_id is referenced in several other tables.
Also, I am curious as to why this is happening :-)
Just in case, here's the Query class:
class Query extends QueryBase
{
function Query($query, &$conn)
{
$this->recordset = array();
$this->has_error=0;
$regs = mysqli_query($conn->getConnection(), $query);
if(!$regs)
{
$this->has_error=1;
return;
}
$index = 0;
$this->current_index=-1;
while(($row = mysqli_fetch_array($regs, MYSQL_ASSOC)))
{
$this->recordset[$index]=$row;
$index++;
}
mysqli_free_result($regs);
}
public function moveNext()
{
if($this->current_index<(sizeof($this->recordset)-1))
{
$this->current_index++;
return 1;
}
else
return 0;
}
public function moveBack()
{
if($this->current_index>=1)
{
$this->current_index--;
return 1;
}
else
return 0;
}
public function recordCount()
{
return sizeof($this->recordset);
}
public function get($field)
{
return $this->recordset[$this->current_index][$field];
}
public function getRow()
{
return $this->recordset[$this->current_index];
}
public function hasError()
{
return $this->has_error;
}
}
Thank you for your help.
It sounds as though you are executing that block of code more than once, can you post the rest of the code? The code that deals with the 'put'?
As a general rule, if the process for checking for the existence of a value and insertion of a value are separate, there can be double entries due to someone accidentally clicking the submit button more than once (for example - or other action? image click...)
It's often a good practice to embed a duplicate submission check so that double submissions are avoided.
I'm not sure if this will fix it for you, but it would be better to return "false;" instead of returning "null" upon failure from your getManageFromName() method.
Another thing to check would your hasErrors() method; it might be returning errors for some reason sometimes?
You might be better off to write it like this:
private function getMangaFromName($name) {
$id = $this->generateId($name);
$mangas = new Query("SELECT * FROM tbl_manga WHERE manga_cid = '" . $this->conn->escapeString($id) . "'", $this->conn);
if($mangas->moveNext()) {
return $mangas->getRow();
} else {
if(!$mangas || $mangas->hasError()) {
logError("getMangaFromName($name): " . $this->conn->getError());
}
return null;
}
}
// Then check it like this:
$manga = $this->getMangaFromName($name);
if ( empty($manga) ) {
$manga = $this->addManga($name);
}
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.