Php, isset - default value, can it be simplified? - php

here is something:
$result = $this->getSomething();
$db = new Db();
$db->save($result['DATA']); // might exists or not
$db->save($result['IP']); // might exists or not
$db->save($result['X']); // might exists or not
but those array keys are not sure to be exists. Of corse I can always write this:
$result = $this->getSomething();
if (!isset($result['DATA']))
{
$result['DATA'] = null;
}
//same for the other keys
$db = new Db();
$db->save($result['DATA']);
$db->save($result['IP']);
$db->save($result['X']);
but its very cumbersome job. Is there any way to simplify this?

Starting at PHP 7 (which every should start using anyway), you can use the new coalesce operator to do this:
$db->save($result['DATA'] ?? null);

$result = $this->getSomething();
$result += array_fill_keys(['DATA', 'IP', 'X'], null);
This populates any of these keys which don't exist with null.
Can even be written as $result = $this->getSomething() + array_fill_keys(..);.
+ array union operator
array_fill_keys

You can do this:
$data = !isset($result['DATA']) ? null : $result['Data'];
$ip = !isset($result['IP']) ? null : $result['IP'];
etc...

It would be better if you would be use OOP way
your method getSomething() must return instance of some class (ex. SomeData) instead of array
/**
* #return SomeData
*/
public function getSomething()
{
/*
* some code
*/
return new SomeData($data, $x, $ip);
}
SomeData class
class SomeData
{
private $data;
private $ip;
private $x;
public function __construct($data, $ip, $x)
{
$this->data = $data;
$this->ip = $ip;
$this->x = $x;
}
/**
* #return mixed
*/
public function getData()
{
return $this->data;
}
/**
* #param mixed $data
*/
public function setData($data)
{
$this->data = $data;
}
/**
* #return mixed
*/
public function getIp()
{
return $this->ip;
}
/**
* #param mixed $ip
*/
public function setIp($ip)
{
$this->ip = $ip;
}
/**
* #return mixed
*/
public function getX()
{
return $this->x;
}
/**
* #param mixed $x
*/
public function setX($x)
{
$this->x = $x;
}
}
And finaly you work with result without worrying about isset and etc.
$result = $this->getSomething();
$db = new Db();
$db->save($result->getData());
$db->save($result->getIp());
$db->save($result->getX());

Related

Best way to create associative array of unique objects in PHP?

I'm trying to create a list of unique objects in PHP.
I want to access each object by a unique name but I also need the object to know its name.
My idea was to create an associative array of objects like this:
class MyObject()
{
public $name;
public $property1;
public $property2;
function __construct($name)
{
$this->name = $name;
}
function doSomething()
{
echo $name.$property1;
}
function doSomethingElse()
{
echo $name.$property2;
}
}
$array = array();
$name = 'example';
$array[$name] = new MyObject($name);
$array[$name]->property1 = 'xyz';
$array[$name]->property2 = 123;
$name = 'test';
$array[$name] = new MyObject($uniqueName);
$array[$name]->property1 = 'abc';
$array[$name]->property2 = 321;
$array['example']->doSomething();
$array['test']->doSomethingElse();
I'm pretty new to PHP and coding in general and I feel like this is a really stupid solution so I wanted to ask if you know any better ways of doing this.
For a simple use-case you've provided, perhaps implementing the predefined interface ArrayAccess may fit the bill. Something like:
class UniqueCollection implements ArrayAccess
{
/**
* #var array<string, MyObject>
*/
private $collection = [];
/**
* #param mixed $offset
*
* #return bool
*/
public function offsetExists($offset): bool
{
return isset($this->collection[$offset]);
}
/**
* #param mixed $offset
*
* #return MyObject|null
*/
public function offsetGet($offset): mixed
{
return $this->collection[$offset] ?? null;
}
/**
* #param mixed $offset
* #param array<mixed, mixed> $values
*
* #return void
*/
public function offsetSet($offset, $values): void
{
if ($offset === null) {
// Error
}
// Uncomment if we don't want to overwrite existing MyObject with the same name,
// eg, throw exception
// if (isset($this->collection[$offset]) {
// // Error
// }
$count($values);
if ($count !== 0 || $count !== 2) {
// Error
}
$myObj = new MyObject($offset);
if ($count) {
// Fetch the values only so we only have to deal with zero-based integer offsets
$values = array_values($values);
// Establish a convention on which index goes to what property
$myObj->property1 = $values[0];
$myObj->property2 = $values[1];
}
$this->collection[$offset] = $myObj;
}
/**
* #param mixed $offset
*
* #return void
*/
public function offsetUnset($offset): void
{
return unset($this->collection[$offset]);
}
}
Use:
$set = new UniqueCollection();
$set['example'] = ['xyz', 123];
$set['test'] = []; // not exactly elegent, but hey ¯\_(ツ)_/¯
$set['test']->property1 = 'abc'; // should work fine, unless we clone the MyObject internally
$set['test']->property2 = 321; // ditto
$set['example']->doSomething();
$set['test']->doSomethingElse();
I haven't tested this so let me know if there are any issues.

How do I pass the controller by other means than the url in PHP MVC

I realize this may be a dumb question, but I am struggling with a custom MVC project I started in order to learn PHP and I was not able to find what I was looking for anywhere else.
My question is, how do I pass the controller to my router, in a way that does not use the url. How would I make it so that the href in my link tags only provide the category and id and yet make it work. At the moment my urls look like this:
website/controller/method/args
ex: website/articles/post/13
I want the url to be like:
website/category/id-or-slug-from-title
I would really appreciate the help, since it's been bugging me for a week now.
Thanks in advance.
Well, it would be hard to describe possible steps in comment section, so I will add an answer.
The following steps will be exemplary and they do not describe the whole process. I will omit some details
Assuming that you're trying to implement MVC, you should have some kind of bootstrap.php (or something like that)
So, let's create our router class
/**
* Current method
* #var string
*/
protected $method;
/**
* Current args
* #var unknown
*/
protected $args = array();
private static $instance;
/**
* That's how we retrieve class instance
* #return app_router_http
*/
public static function getInstance()
{
if (!isset(static::$instance))
{
static::$instance = new self;
}
return static::$instance;
}
private function __clone() {}
/**
* #throws app_exception
*/
private function __construct()
{
/* парсим текущий урл */
$url = parse_url (isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : "/", PHP_URL_PATH);
$url = preg_replace("/\\/{2,}/i", "/", $url);
/* Let's retrieve static route (for example - /category/id-or-slug-from-title) */
if ($_route = $this->getStaticRoute($url))
{
if ($_route->hasErrors())
{
throw new app_exception($_route->getErrors(";"));
}
$this
->setController($_route->getController())
->setMethod($_route->getMethod())
->setArgs($_route->getArgs() ? $_route->getArgs() : null)
;
return;
}
/* Let's retrive dynamic route, because we didn't fing static */
if ($_route = $this->getDynamicRoute($url))
{
if ($_route->hasErrors())
{
throw new app_exception($_route->getErrors(";"));
}
$this
->setController($_route->getController())
->setMethod($_route->getMethod())
->setArgs($_route->getArgs() ? $_route->getArgs() : null);
return;
}
throw new app_exception("Can't found any route objects", 503);
}
/**
* #param string $controller
* #return Router
*/
public function setController($controller)
{
$this->controller = $controller;
return $this;
}
/**
* #return string
*/
public function getController()
{
return $this->controller;
}
/**
* #param string $method
* #return Router
*/
public function setMethod($method)
{
$this->method = $method;
return $this;
}
/**
* #return string
*/
public function getMethod()
{
return $this->method;
}
/**
* #param array $args
* #return Router
*/
public function setArgs(array $args = null)
{
if (isset($args))
{
$this->args = $args;
}
return $this;
}
/**
* #return mixed
*/
public function getArgs()
{
return $this->args;
}
/**
* #param string $route
* #param string $controller
* #param string $method
* #param string $objectId
* #return Route|NULL
*/
public function getStaticRoute($route = null, $controller = null, $method = null, $objectId = null)
{
$db = new DB(); //Some class for db connections
if (isset($route) && !isset($controller) && !isset($method) && !isset($objectId))
{
$selector = "SELECT * FROM `routes` WHERE `route` = '".$db->escape($route)."'";
}
if (!isset($route) && isset($controller) && isset($method) && isset($objectId))
{
$selector = "SELECT * FROM `routes` WHERE `controller` = '".$db->escape($controller)."' && `method` = '".$db->escape($method)."' && `objectId` = '".$db->escape($objectId)."'";
}
if (!isset($selector))
{
throw new app_exception(get_class($this)."::getStaticRoute incorrect params", 503);
}
if ($db->query($selector)->rows())
{
$row = $db->fetch();
$object = new Router();
$object->setAttributes($row);
if (!$object->hasErrors())
{
$object->setController("{$object->getController()}");//Here we are setting our awesome controller
if (!$this->isControllerExist($object->getController()))
{
return $object->addError("Controller {$object->getController()} missing (static route: #{$object->getId()})");
}
if (!$this->isMethodExist($object->getController(), $object->getMethod()))
{
return $object->addError("Method {$object->getMethod()} missing (controller: {$object->getController()}) (static route: #{$object->getId()})");
}
}
return $object;
}
return null;
}
/**
* #param string $path
* #return Router
*/
public function getDynamicRoute($path)
{
$object = new Router;
//Here we will assume that our url looks like this /controller/method/args
/* Url fragments */
$fragments = explode("/", substr($path, 1));
//Removing trailing slash
if (!$fragments[sizeof($fragments)-1])
{
unset($fragments[sizeof($fragments)-1]);
}
if (!isset($fragments[0]))
{
$fragments[0] = APP_ROUTE_DEFAULT_CONTROLLER; //Some kind of magic constant
}
$controller = $fragments[0];
if(!class_exists($controller)
{
throw new Exception("Ooops, controller not found", 404);
}
array_shift($fragments); //We don't need controller element anymore
for ($i = 0; $i <= 1; $i++)
{
if ($i == 0)
{
$method = APP_ROUTE_DEFAULT_METHOD;//We also need to handle urls like /news. For example, each controllers has default method index()
}
else
{
$method = $fragments[0];
}
if ($this->isControllerExist($controller) && $this->isMethodExist($controller, $method))
{
return
$object
->setController($controller)
->setMethod($method);
}
}
return $object->addError("Can't route to <strong>".implode("/", $fragments)."</strong> (dynamic route for module <strong>{$module}</strong>)");
}
/**
* #param string $controller
* #return boolean
*/
public function isControllerExist($controller = null)
{
if (!isset($controller))
{
$controller = $this->getController();
}
return class_exists($controller);
}
/**
* #param string $controller
* #param string $method
* #return boolean
*/
public function isMethodExist($controller = null, $method = null)
{
if (!isset($controller))
{
$controller = $this->getController();
}
if (!isset($method))
{
$method = $this->getMethod();
}
return method_exists($controller, $method);
}
public function run()
{
$_str = $this->getController();
$controller = new $_str;
$return = call_user_func_array(array($controller, $this->getMethod()), $this->getArgs());
return $return;
}
}
So, in bootstrap.php you just need to need to make a call Router::getInstance()->run()
Static route will try to pass your params
For dynamic routes, you always can read args from $_REQUEST
P.S. To be true, this is a cutted examaple made from my old project

How to add more value to a single array in the same function?

When I try the following code, I get just the first output. I wanted to append all the outputs to the array self::$results so that the same function will return that array, but after running the script, the function returns the array but only the first output. That means, it appended only the the first output.
<?php
/**
* #author Ewoenam
* #copyright 2014
*
* #odj class book
*/
class book
{
/**
* #array static $alpha; holds array of accepted words
*/
public static $alpha = array();
/**
* #array static $result; holds array of correct answers
*/
public static $results;
/**
* #string protected $word; parametr
*/
protected $word;
/**
* #var static $alpha; holder of class instance getter
* returns self::getInstance
*/
public static $init;
/**
* #method static getInstance(); class instance getter
* returns self();
*/
function __construct()
{
self::$alpha = array('love','life','health','power','money','God');
}
public static function getInstance()
{
if (self::$init === null)
{
self::$init = new self();
return self::$init;
}
}
/**
* #method static check()
* takes 1 param; self::$word
* returns bool
*/
public static function check($word)
{
for($i=0;$i<=count(self::$alpha)-1;$i++)
{
if(similar_text($word,self::$alpha[$i]) === strlen($word))
{
return true;
}
}
}
/**
* #method static result()
* takes 1 param; self::check()
* returns bool
*/
public static function result($bool)
{
if($bool === true)
{
return 'correct';
}
else
{
return 'wrong';
}
}
/**
* #method static getter()
* takes 1 param; array of words to be searched
* returns array self::$results
*/
public static function getter($array)
{
self::$results = array();
for($i = 0;$i<=count($array)-1;$i++)
{
// i want to add more thn one answers to to $result array but i get only the first answer.
//how do i ddo it?
self::$results[] = self::result(book::check($array[$i]));
return self::$results;
}
}
}
$array = array('love','ama','kofi','money','health','God');
print_r(book::getInstance()->getter($array));
var_dump(book::check('love')) ;
?>
You're returning from $getter inside the loop, so it returns after adding the first element. You should finish the loop and then return:
public static function getter($array)
{
self::$results = array();
for($i = 0;$i<=count($array)-1;$i++)
{
self::$results[] = self::result(book::check($array[$i]));
}
return self::$results;
}
You have your return in the for loop. That's why when it goes through the first time, it returns the function right there instead of continuing the loop.
Change your function to this and it will work how you want it to:
/**
* #method static getter()
* takes 1 param; array of words to be searched
* returns array self::$results
*/
public static function getter($array)
{
self::$results = array();
for($i = 0;$i<=count($array)-1;$i++)
{
// i want to add more thn one answers to to $result array but i get only the first answer.
//how do i ddo it?
self::$results[] = self::result(book::check($array[$i]));
}
return self::$results;
}

How to construct constants in yii

I am using Yii for my web application. In this I kept Constants class in model and extended
from CUserIdentity like..
class Constants extends CUserIdentity
{
CONST ACCOUTN_ONE = 1;
CONST ACCOUTN_TWO = 2;
CONST ACCOUTN_THREE = 3;
}
Here I can access constants like Constants::ACCOUTN_ONE and it will return correct result as 1
But when I start construct constants dynamically means..
$type = 'ONE';
$con = "Constants::ACCOUTN_".$type;
echo $con;
It will dispaly as Constants::ACCOUTN_ONE;
I am expecting here 1
Please correct me if any mistake..
$type = 'ONE';
$con = "Constants::ACCOUTN_".$type;
echo Constant($con);
$type = 'ONE'; // You created string
$con = "Constants::ACCOUTN_".$type; // Created other string
echo $con; // Printed it
You just printed string without evaluating it.
Yes, of couse it will dispaly as Constants::ACCOUTN_ONE;
You need to evaluate your code with eval()(bad), or use this scheme:
echo Constant($con);
I do this with
a class for it a while back:
/**
* Lots of pixie dust and other magic stuff.
*
* Set a global: Globals::key($vlaue); #return void
* Get a global: Globals::key(); #return mixed|null
* Isset of a global: Globals::isset($key); #return bool
*
* Debug to print out all the global that are set so far: Globals::debug(); #return array
*
*/
class Globals
{
private static $_propertyArray = array();
/**
* Pixie dust
*
* #param $method
* #param $args
* #return mixed|bool|null
* #throws MaxImmoException
*/
public static function __callStatic($method, $args)
{
if ($method == 'isset') {
return isset(self::$_propertyArray[$args[0]]);
} elseif ($method == 'debug') {
return self::$_propertyArray;
}
if (empty($args)) {
//getter
if (isset(self::$_propertyArray[$method])) {
return self::$_propertyArray[$method];
} else {
return null; //dont wonna trow errors when faking isset()
}
} elseif (count($args) == 1) {
//setter
self::$_propertyArray[$method] = $args[0];
} else {
throw new Exception("Too many arguments for property ({$method}).", 0);
}
}
}

How do I refer to a "this" class member in a function parameter?

I currently have a function where I'm trying to refer to the $id in the class but it doesn't work:
public function getCourseInfo($cid = $this->id, $all = false)
{
}
This is my class:
class Course
{
protected $course;
protected $id;
public function __construct($cid)
{
$id = $cid;
$this->course = $this->getCourseInfo($this->id);
}
public function getCourseInfo($cid = $this->id, $all = false)
{
}
}
You haven't established $this->id yet. :)
wrong
$id = $cid;
right
$this->id = $cid;
You're also missing a closing curly-brace on your class.
Everyone in this thread is giving correct answers but no one gave a full code sample hence I'll post my suggestion:
class Course
{
/**
* #var int
*/
protected $_courseId;
/**
* #var array
*/
protected $_course;
/**
* Class constructor
*
* #param int $courseId Course ID
* #return Course
*/
public function __construct($courseId)
{
$this->_courseId = (int) $courseId;
}
/**
* Get course information
*
* #param bool $all ...
* #return array
*/
public function getCourseInfo($all = false)
{
if ($this->_course === null) {
// use $this->_courseId as needed
$this->_course = ... // populate course info
}
return $this->_course;
}
}
As you'll notice I've omitted the course id parameter from getCourseInfo() simply because it's not needed if you instantiate the class with a course id.
Secondly, I don't think you should call getCourseInfo in the constructor because the information will only be needed at a later point. Also, I added "caching" to the function so that you don't fetch data twice.
Obviously there's a high chance that I could be wrong having not seen your code but I feel this is a better structure of the code.
You need to set the $id first in your constructor.
class Course
{
protected $course;
protected $id;
}
public function __construct($cid)
{
$this->id = $cid;
$this->course = $this->getCourseInfo($id);
}
Try this
public function getCourseInfo($cid = 'default', $all = false)
{
$cid = $cid == 'default' ? $this->id : $cid;
}
Or you need completey change your class
class Course
{
protected $course;
protected $id;
public function __construct($cid)
{
$this->id = $cid;
$this->course = $this->getCourseInfo();
}
public function getCourseInfo($course_id = 0, $all = false)
{
$course_id = !$course_id ? $this->id : $course_id;
//do Somthing with
//return var;
}
No, this isn't possible, as stated on the Function arguments manual page:
The default value must be a constant
expression, not (for example) a
variable, a class member or a function
call.
Instead you could either simply pass in null as the default and update this within your function...
class Course
{
protected $course;
protected $id;
public function __construct($cid)
{
$this->id = $cid;
$this->course = $this->getCourseInfo($this->id);
}
function getCourseInfo($cid = null, $all = false) {
$cid = isset($cid) ? $cid : $this->id;
....
}
}

Categories