How can I make big multidimensional variables seem shorter in PHP? - php

I'm creating a php class which is getting slightly out of hand the deeper it gets.
Here's an example:
unset($this->file[$key]->inspect->formats);
unset($this->file[$key]->inspect->tags);
unset($this->file[$key]->inspect->chapters);
unset($this->file[$key]->inspect->annotations);
unset($this->file[$key]->inspect->automatic_captions);
unset($this->file[$key]->inspect->subtitles);
$this->file[$key]->inspect->name = trim($this->file[$key]->inspect->name);
$this->file[$key]->inspect->artist = trim($this->file[$key]->inspect->artist);
Instead of writing $this->file[$key]->inspect for every single variable I want to use is there a way I can set a variable e.g $inspect to take this place?
So that when I write $inspect->subtitles it'll know what I really mean and affect the main $this->file[$key]->inspect->subtitles?

$inspect = &$this->file[$key]->inspect;
declare this. Now you can set your data like this
$inspect->formats = 'format';
$inspect->subs = 'subs';
// ...
adding the & you will affect the variable and not only a copy of this variable
here are explanations about references http://php.net/manual/en/language.references.whatare.php

One approach you could use is replicate the object_get() helper method from Laravel that will fetch elements based on dot notation.
/**
* Get an item from an object using "dot" notation.
*
* #param object $object
* #param string $key
* #param mixed $default
* #return mixed
*/
function object_get($object, $key, $default = null)
{
if (is_null($key) || trim($key) == '') return $object;
foreach (explode('.', $key) as $segment)
{
if ( ! is_object($object) || ! isset($object->{$segment}))
{
return value($default);
}
$object = $object->{$segment};
}
return $object;
}
$ob = new StdClass();
$ob->property->name->value = 'Lol';
echo object_get($ob, 'property.name.value');
Unfortunately there'd be a bit of extra implementation if the $object->property was an array like in your example.

Related

Unit test: using the proper terminology for mocking/stubbing

After fundamental changes on my project system architecture, I find myself in a situation where I would need to create "fake" implementation in order to test some functionality that used to be public like the following:
/**
* Display the template linked to the page.
*
* #param $newSmarty Smarty object to use to display the template.
*
* #param $parameters associative Array containing the values to pass to the template.
* The key is the name of the variable in the template and the value is the value of the variable.
*
* #param $account child class in the AccountManager hierarchy
*
* #param $partialview String name of the partial view we are working on
*/
protected function displayPageTemplateSmarty(Smarty &$newSmarty, array $parameters = array(), AccountManager $account = NULL, string $partialview = "")
{
$this->smarty = $newSmarty;
if (is_file(
realpath(dirname(__FILE__)) . "/../../" .
Session::getInstance()->getCurrentDomain() . "/view/" . (
!empty($partialview) ?
"partial_view/" . $partialview :
str_replace(
array(".html", "/"),
array(".tpl", ""),
Session::getInstance()->getActivePage()
)
)
)) {
$this->smarty->assign(
'activeLanguage',
Session::getInstance()->getActiveLanguage()
);
$this->smarty->assign('domain', Session::getInstance()->getCurrentDomain());
$this->smarty->assign(
'languages',
Languagecontroller::$supportedLanguages
);
$this->smarty->assign(
'title',
Languagecontroller::getFieldTranslation('PAGE_TITLE', '')
);
$this->smarty->assign_by_ref('PageController', $this);
$htmlTagBuilder = HTMLTagBuilder::getInstance();
$languageController = LanguageController::getInstance();
$this->smarty->assign_by_ref('htmlTagBuilder', $htmlTagBuilder);
$this->smarty->assign_by_ref('languageController', $languageController);
if (!is_null($account)) {
$this->smarty->assign_by_ref('userAccount', $account);
}
if (!is_null($this->menuGenerator)) {
$this->smarty->assign_by_ref('menuGenerator', $this->menuGenerator);
}
foreach ($parameters as $key => $value) {
$this->smarty->assign($key, $value);
}
$this->smarty->display((!empty($partialview) ?
"partial_view/" . $partialview :
str_replace(
array(".html", "/"),
array(".tpl", ""),
Session::getInstance()->getActivePage()
)
));
}
}
In this case, the PageController class used to be called directly in controllers, but is now an abstract class extended by the controllers and my unit tests can no longer access the method.
I also have methods like this one in my new session wrapper class that can only be used in very specific context and for which I really need to create fake page implementation to test them.
/**
* Add or update an entry to the page session array.
*
* Note: can only be updated by the PageController.
*
* #param $key String Key in the session array.
* Will not be added if the key is not a string.
*
* #param $value The value to be added to the session array.
*
* #return Boolean
*/
public function updatePageSession(string $key, $value)
{
$trace = debug_backtrace();
$updated = false;
if (isset($trace[1]) and
isset($trace[1]['class']) and
$trace[1]['class'] === 'PageController'
) {
$this->pageSession[$key] = $value;
$updated = true;
}
return $updated;
}
Even though I read a few article, it is still quite unclear in my mind if those fake classes should be considered as "stub" or a "mock" (or even "fake", "dummy" and so on).
I really need to use the proper terminology since my boss is expecting me (in a close future) to delegate most of my workload with oversea developers.
How would you call those fake class implementation created solely for testing purpose in order to be self-explanatory?
Gerard Meszaros explains the terminology of dummies, stubs, spies, mocks, and fakes here.
You can find examples from the PHP world here.

Doing 1 if code with 2 different condition variables... Or something like this

I wanted to make function with this possible argument:s
leFunction ((string) $type,(string) $data) , this was simple, switch defining $return by given type (doing whatever switch can possibly do with $data). Eg I can send year 1865 as $data and if $type is "fromYear" and $data is_numeric, it'll do whatever it's supposed to do.
leFunction ((string) $type,(array) $data) , this is where it gets tricky. Idea was that with this second option I can pass same args as ("fromYear", array("year" => 1865, ....) ) then check if is_numeric($data["year"]) and continue with same code.
But when I combined both pieces together I got this not so great if:
if (is_numeric($data) || (is_array($data) && isset($data["year"])))
Then I replaced second part with function from parent obj :
if (is_numeric($data) || is_numeric($this->setIfGet("year", $data)))
func:
/**
* #param $needle string key
* #param $haystack array source
* #param bool $null make true if value of $needle could be null
* #return bool
*/
protected function setIfGet($needle, $haystack, $null = false)
{
return (isset($haystack[$needle])) ? $haystack[$needle] : ($null) ? (array_key_exists($needle, $haystack) ? $haystack[$needle] : false): false;
}
But here's the problem: If I want to echo year from $data argument I need to do
if (...) {
echo $data;
...
}
but it wont work if $data was array and year which I want to echo is in
$data["year"];
How can I get it work for both cases for 1 echo.
I think I found the answer to my own question:
if (is_numeric($newData = $data) || is_numeric($newData = self::setIfGet("year", $data))){
echo $newData;

Use JSONPath to set a value in an array

I'm trying to process some decoded json data using the PHP implementation of JSONPath (http://goessner.net/articles/JsonPath/).
I'm fine with using an expression to FIND data in the decoded JSON, but I'd like to be able to SET data using a JSONPath expression. Has anyone been able to do this in PHP using JSONPath, if so, how?
It seems that this implementation of JSONPath does not support set operations.
I've written a simple function that can be added to jsonPath.php to add this functionality. I've pasted it here in case it might be of use to anyone else:
/**
* #param array $obj Decoded json file to alter
* #param string $expr JSONPath expression (http://goessner.net/articles/JsonPath/)
* #param mixed $value Value to set all matching entries to
*/
function jsonPathSet(&$obj, $expr, $value)
{
$paths = jsonPath($obj, $expr, array('resultType' => 'PATH'));
$jsonPath = new JsonPath();
foreach ($paths as $path) {
$p = $jsonPath->normalize($path);
$keys = explode(';', $p);
$current = &$obj;
foreach ($keys as $key) {
if($key=='$') {
continue;
} else if (is_array($current)) {
$current = &$current[$key];
} else {
$current = &$current->$key;
}
}
$current = $value;
}
}
Thanks to Mike Brant for the suggestions!
In briefly looking at the documentation, it would appear that JSONPath doesn't support set operations. If one were so inclined, I would imagine that you could modify JSONPath to optionally return an array of pointers (i.e. object references) as a resultType such that you could operate on the values directly.

Isset with (n-depth) object getters

A Zend-Framework project that uses Doctrine.
Data comes in form of objects. In my Zend View i access it like
$this->personData->getPersonAdress()->getPersonStreet();
Since its possible that a Person doesnt have an associated adress, we have to check if the personadress exists and if the personStreet is filled before echoing because otherwise an echoing NULL error may occur.
So we use some IFs with isset:
<? if($this->personData->getPersonaddress()) echo $this->personData->getPersonaddress()->getPersonstreet(); else echo "''"?>
Example (worst case):
<?
if(isset($this->address[0]) && is_object($this->address[0]))
{
$help2=$this->address[0]->getAddress();
if (isset($help2) && is_object($help2))
{
$help=$this->address[0]->getAddress()->getCountry();
if (isset($help) && is_object($help) && $help->getCountryId())
{
echo $this->address[0]->getAddress()->getCountry()->getCountryId();
}
}
}
?>
We need a solution or eventualla a Zend_view helper to simplify the procedure of echoing these values.
Any ideas would be highly appreciated..
You can tell Doctrine to return an array, instead of mapped objects, by using HYDRATE_ARRAY.
That way, isset() can be called directly.
I solved the issue myself by implementing a Zend View Helper that prints out every value ignoring errors that could occur by non-object properties or NULL associations.
This should be useful to everyone working with Zend Framework + Doctrine 2.
Usage
Instead of
$this->address[0]->getAddress()->getCountry()->getCountryId()
Use (it delivers the value or default (0, third parameter) if not set)
$this->Printsafe($this->address[0], "getAddress/getCountry/getCountryId", 0)
Code following
class Printsafe extends Zend_View_Helper_Abstract {
public function isObjectOrSet($data, $properties)
{
if ($data != null)
{
if(is_object($data))
{
if (isset($properties[0]))
{
$actual_property = array_shift($properties);
return $this->isObjectOrSet($data->{$actual_property}(), $properties);
}
}
elseif(isset($data))
{
return $data;
}
else
return null;
}
else
return null;
}
/**
* Wants data and getters + properties
* Tests if they are set and returns requested value
* Returns empty value (instead of NULL) if any error occurs
*
* #param mixed $data - Example $this->personData
* #param string $properties - Example "getPersontype/getNotation"
* #param mixed $default - return value if not set. Default is empty string.
*
* #return mixed $value
*/
public function printsafe($data = null, $properties='', $default = '') {
$return = null;
if ($data != null)
{
if ($properties != '')
{
$prop_array = explode("/", $properties);
$return = $this->isObjectOrSet($data, $prop_array);
}
}
if ($return == null)
$return = $default;
return $return;
}
}
You could prefix the chain with #, which suppresses error output - then you wouldn't need to check it at all. If it doesn't exist, it will simply not output anything.
<?php echo #$this->personData->getPersonAdress()->getPersonStreet(); ?>
It isn't usually recommended to use the # operator, but in this case it seems like an appropriate solution. (The disadvantage is that you would miss other errors that may come from this line)

PHP memory references

I am wondering this question for a long time, how does PHP handle references are they a good idea to use and I can't explain better than using an example, lets look at the following class and then # the comment of the setResult method.
Lets imagine we are using a model view controller framework and we are building a basic AjaxController, we only got 1 action method (getUsers) so far. Read the comments, and I hope my question is clear, how does PHP handle these kind of situations and is it true what I wrote about the x times in the memory # the setResult docblock.
class AjaxController{
private $json = array(
'result' => array(),
'errors' => array(),
'debug' => array()
);
/**
* Adds an error, always displayed to users if any errors.
*
* #param type $description
*/
private function addError($description){
$this->json['errors'][] = $description;
}
/**
* Adds an debug message, these are displayed only with DEBUG_MODE.
*
* #param type $description
*/
private function addDebug($description){
$this->json['debug'][] = $description;
}
/**
* QUESTION: How does this go in memory? Cause if I use no references,
* the array would be 3 times in the memory, if the array is big (5000+)
* its pretty much a waste of resources.
*
* 1st time in memory # model result.
* 2th time in memory # setResult ($resultSet variable)
* 3th time in memory # $this->json
*
* #param array $resultSet
*/
private function setResult($resultSet){
$this->json['result'] = $resultSet;
}
/**
* Gets all the users
*/
public function _getUsers(){
$users = new Users();
$this->setResult($users->getUsers());
}
public function __construct(){
if(!DEBUG_MODE && count($this->json['debug']) > 0){
unset($this->json['debug']);
}
if(count($this->json['errors']) > 0){
unset($this->json['errors']);
}
echo json_encode($this->json);
}
}
Another simple example: What would be better to use technique A:
function example(){
$latestRequest = $_SESSION['abc']['test']['abc'];
if($latestRequest === null){
$_SESSION['abc']['test']['abc'] = 'test';
}
}
Or technique B:
function example(){
$latestRequest =& $_SESSION['abc']['test']['abc'];
if($latestRequest === null){
$latestRequest = 'test';
}
}
Thanks for reading and advise :)
In short: don't use references.
PHP copies on write. Consider:
$foo = "a large string";
$bar = $foo; // no copy
$zed = $foo; // no copy
$bar .= 'test'; // $foo is duplicated at this point.
// $zed and $foo still point to the same string
You should only use references when you need the functionality that they provide. i.e., You need to modify the original array or scalar via a reference to it.

Categories