PHPUnit test doesn't 'go' into if conditional - php

public function test_cleansUpHouses()
{
$houseWithNoHP = new \DeadStreet\ValueObject\House\House();
$houseWithNoHP->setHitPoints(0);
$houseWithNoHP->setCurrentAttackers(1);
$houseWithHP = new \DeadStreet\ValueObject\House\House();
$houseWithHP->setCurrentAttackers(1);
$houseWithHPWasNotAttacked = new \DeadStreet\ValueObject\House\House();
$houseWithHPWasNotAttacked->setCurrentAttackers(1);
$houseCollection = new \DeadStreet\ValueObject\House\Collection(
[$houseWithNoHP, $houseWithHP, $houseWithHPWasNotAttacked]
);
$this->mockHouseModel->expects($this->at(0))
->method('hasBeenAttacked')
->with($houseWithNoHP)
->willReturn(true);
$this->mockHouseModel->expects($this->at(1))
->method('hasBeenAttacked')
->with($houseWithHP)
->willReturn(true);
$this->mockHouseModel->expects($this->at(2))
->method('hasBeenAttacked')
->with($houseWithHPWasNotAttacked)
->willReturn(false);
$this->mockHouseModel->expects($this->at(0))
->method('requiresDestroying')
->with($houseWithNoHP)
->willReturn(true);
$this->mockHouseModel->expects($this->at(1))
->method('requiresDestroying')
->with($houseWithHP)
->willReturn(false);
$expectedHouses = [$houseWithHP, $houseWithHPWasNotAttacked];
$cleanedUpHouses = $this->createObject()->cleanUpHouses($houseCollection);
$this->assertEquals($expectedHouses, $cleanedUpHouses->getHouses());
}
private function createObject()
{
return new Collection($this->mockHouseModel);
}
And here's the model under test
public function cleanUpHouses(\DeadStreet\ValueObject\House\Collection $collection)
{
foreach($collection->getHouses() as $key => $house) {
if(!$this->houseModel->hasBeenAttacked($house)) {
break;
}
if($this->houseModel->requiresDestroying($house)) {
unset($collection->getHouses()[$key]);
}
}
return $collection;
}
However, this line if($this->houseModel->requiresDestroying($house)) { is never being returned as true, even though I have the line
$this->mockHouseModel->expects($this->at(0))
->method('requiresDestroying')
->with($houseWithNoHP)
->willReturn(true);
The error I'm getting.
1) DeadStreet\Model\House\Collection_Test::test_cleansUpHouses
Failed asserting that two arrays are equal.
--- Expected
+++ Actual
## ##
Array (
0 => DeadStreet\ValueObject\House\House Object (...)
1 => DeadStreet\ValueObject\House\House Object (...)
+ 2 => DeadStreet\ValueObject\House\House Object (...)
)

Are you sure the code is working correctly?
This line
unset($collection->getHouses()[$key]);
uses a copy of your array to execute an unset on it.
Example:
class Test
{
private $array = array("a" => 1, "b" => 2);
public function unsetArray()
{
unset($this->getArray["a"]);
}
public function getArray()
{
return $this->array;
}
}
$test = new Test();
$test->unsetArray();
The method unsetArray always resets on a copy here, not the actual array. If you want something like that. You have to use the actual array like this.
public function unsetArray()
{
unset($this->array["a"]);
}

Related

PHP - iterate through different value types

I have object of class $values like:
Array
(
[0] => App\ValueObject\Features Object
(
[feature:App\ValueObject\Features:private] => CONNECT_NETWORKS_ON_SIGN_UP
[value:App\ValueObject\Features:private] => 1
)
[1] => App\ValueObject\Features Object
(
[feature:App\ValueObject\Features:private] => SHOW_BILLING
[value:App\ValueObject\Features:private] => 1
)
[2] => App\ValueObject\Features Object
(
[feature:App\ValueObject\Features:private] => FEATURE_FLAGS
[value:App\ValueObject\Features:private] => 'activity'
)
)
All array keys are returning boolean type value expect one, which returns string value.
My result with the code:
$arrays = array_map(
function($value) { return [strtolower((string) $value->getFeature())]; },
iterator_to_array($values)
);
return array_merge(...$arrays);
returns list of feature names like:
"features": [
"connect_networks_on_sign_up",
"show_billing",
"feature_flags"
]
What I want to edit is that for the last one we write its value NOT feature name ($value->getValue())
I am assuming that using in_array() PHP function would be the best approach here but I can't find a way to use it within my current method.
Tried with foreach() loop but nothing happens, like it's something wrong:
$features = [];
foreach ($values as $value)
{
$setParam = $value->getFeature();
if ($value == 'FEATURE_FLAGS') {
$setParam = $value->getValue();
}
$features[] = strtolower((string) $setParam);
}
return $features;
Can someone help?
Thanks
You should probably operate on the feature code FEATURE_FLAGS, rather than assuming that the last feature in the array always contains the flags. Using your existing code, that could be as simple as:
$arrays = array_map(
function($value)
{
/*
* If the current Features object has the feature code FEATURE_FLAGS,
* return the value itself, otherwise return the feature code in lowercase
*/
return ($value->getFeature() == 'FEATURE_FLAGS') ? [$value->getValue()]:[strtolower((string) $value->getFeature())];
},
iterator_to_array($values)
);
If you want to define an array of feature codes that you need to treat this way, you can define it internally in the callback, but it is probably a better idea to define it externally. You can then pass it into the callback with use
/*
* Define an array of feature codes that we want to return
* values for
*/
$valueCaptureFeatures = ['FEATURE_FLAGS'];
$arrays = array_map(
function($value) use ($valueCaptureFeatures) // <-- Put our $valueCaptureFeatures in the scope of the callback
{
/*
* If the current Features object has a feature code in the $valueCaptureFeatures array,
* return the value itself, otherwise return the feature code in lowercase
*/
return (in_array($value->getFeature(), $valueCaptureFeatures)) ? [$value->getValue()]:[strtolower((string) $value->getFeature())];
},
iterator_to_array($values)
);
Working example:
// Mock the Features class
class Features
{
private $feature;
private $value;
public function __construct($feature, $value)
{
$this->feature = $feature;
$this->value = $value;
}
public function getFeature()
{
return $this->feature;
}
public function setFeature($feature): void
{
$this->feature = $feature;
}
public function getValue()
{
return $this->value;
}
public function setValue($value): void
{
$this->value = $value;
}
}
// Mock an iterator with test Feature instances
$values = new ArrayIterator( [
new Features('CONNECT_NETWORKS_ON_SIGN_UP', 1),
new Features('SHOW_BILLING', 1),
new Features('FEATURE_FLAGS', 'activity')
]);
/*
* Define an array of feature codes that we want to return
* values for
*/
$valueCaptureFeatures = ['FEATURE_FLAGS'];
$arrays = array_map(
function($value) use ($valueCaptureFeatures) // <-- Put our $valueCaptureFeatures in the scope of the callback
{
/*
* If the current Features object has a feature code in the $valueCaptureFeatures array,
* return the value itself, otherwise return the feature code in lowercase
*/
return (in_array($value->getFeature(), $valueCaptureFeatures)) ? [$value->getValue()]:[strtolower((string) $value->getFeature())];
},
iterator_to_array($values)
);
$output = array_merge(...$arrays);
$expectedResult = [
'connect_networks_on_sign_up',
'show_billing',
'activity'
];
assert($output == $expectedResult, 'Result should match expectations');
print_r($output);

Shorted similar commands in PHP function

I want to short this function. I have also published a little part of it, but it is every time the same principle:
if(in_array($infinitiveVerb,
IrregularExceptionGroup::$name_in_lowercase)) {
$exceptionmodel = ExceptionModel::NAME_IN_UPPERCASE;
}
php function
function finding_exception_model(InfinitiveVerb $infinitiveVerb)
{
$exceptionmodel = ExceptionModel::NO_EXCEPTIONS;
if (in_array($infinitiveVerb, IrregularExceptionGroup::$aller)) {
$exceptionmodel = ExceptionModel::ALLER;
}
if (in_array($infinitiveVerb, IrregularExceptionGroup::$avoir_irr)) {
$exceptionmodel = ExceptionModel::AVOIR_IRR;
}
if (in_array($infinitiveVerb, IrregularExceptionGroup::$etre_irr)) {
$exceptionmodel = ExceptionModel::ETRE_IRR;
}
return new ExceptionModel($exceptionmodel);
}
ExceptionModel.php
class ExceptionModel extends Enum
{
const NO_EXCEPTIONS = 'no exceptions';
const ALLER = 'aller';
const AVOIR_IRR = 'avoir_irr';
const ETRE_IRR = 'etre_irr';
}
How is this possible?
The only thing I can see and would change here, is to just put every irregularExceptionGroup into an array, like this:
function finding_exception_model(InfinitiveVerb $infinitiveVerb)
{
$exceptionmodel = ExceptionModel::NO_EXCEPTIONS;
$irregularExceptionGroupArray = [
ExceptionModel::ALLER => IrregularExceptionGroup::$aller,
ExceptionModel::AVOIR_IRR => IrregularExceptionGroup::$avoir_irr,
ExceptionModel::ETRE_IRR => IrregularExceptionGroup::$etre_irr,
];
foreach($irregularExceptionGroupArray as $exceptionModel => $irregularExceptionGroup){
if(in_array($infinitiveVerb, $irregularExceptionGroup)){
$exceptionmodel = $exceptionModel;
//break; //If you don't want to overwrite the variable, just uncomment this
}
}
return new ExceptionModel($exceptionmodel);
}
You can combine all exceptions into one lookup, instead of this in_array checks all the time. (Note: IF off course the InfinitVerb has a __toString of some kind)
For example IrregularExceptionGroup::$aller contains: ['aller', 'allez', 'je suis', 'paris'] and IrregularExceptionGroup::$avoir_irr contains ['some', 'more', 'stuff']
Change it to:
IrregularExceptionGroup::$allExceptions = [
'aller' => ExceptionModel::ALLER,
'allez' => ExceptionModel::ALLER,
'je suis' => ExceptionModel::ALLER,
'paris' => ExceptionModel::ALLER,
'some' => ExceptionModel::AVOIR_IRR,
'more' => ExceptionModel::AVOIR_IRR,
'stuff' => ExceptionModel::AVOIR_IRR
];
function finding_exception_model(InfinitiveVerb $infinitiveVerb)
{
$ex = ExceptionModel::NO_EXCEPTIONS;
if (array_key_exists($infinitiveVerb, IrregularExceptionGroup::$allExceptions)) {
$ex = IrregularExceptionGroup::$allExceptions[$infinitiveVerb];
}
return new ExceptionModel($ex);
}
And you could even make it "shorter" by using the ternary operator:
function finding_exception_model(InfinitiveVerb $infinitiveVerb)
{
return new ExceptionModel(isset(IrregularExceptionGroup::$allExceptions[$infinitiveVerb]) ? IrregularExceptionGroup::$allExceptions[$infinitiveVerb] : ExceptionModel::NO_EXCEPTIONS);
}
or PHP 7:
function finding_exception_model(InfinitiveVerb $infinitiveVerb)
{
return new ExceptionModel( IrregularExceptionGroup::$allExceptions[$infinitiveVerb] ?? ExceptionModel::NO_EXCEPTIONS);
}

Generating a php object, two levels deep

I'm new to php - objects and arrays, especially. Coming from a JavaScript world, I'm having a modicum of trouble understanding the right way to construct objects, that may easily be iterated.
I'd like to create an object (or array - although I suspect an object would be more suitable) with the following structure:
$client_body:
$cst:
$title: 'Unique string'
$copy: function_result()
$ser:
$title: 'Unique string'
$copy: function_result()
$imp
$title: 'Unique string'
$copy: function_result()
...
I've been trying with variations on the following, but with numerous errors:
$client_body = new stdClass();
$client_body->cst->title = 'Client case study';
$client_body->cst->copy = get_field('client_cst');
$client_body->ser->title = 'Our service';
$client_body->ser->copy = get_field('client_ser');
...
And it seems that, using this approach, I'd have to use a new stdClass invocation with each new top-level addition, which seems a little verbose.
Could someone point me in the right direction?
You can just typecast an array to an object:
$client_body = (object)array(
"cst" => (object)array(
"title" => "Unique string",
"copy" => function_result()
)
);
You can try this object class more OOP:
<?php
class ClientBody{
protected $cst;
protected $ser;
protected $imp;
public function __construct($cst = '', $ser ='', $imp = '')
{
$this->cst = $cst;
$this->ser = $ser;
$this->imp = $imp;
}
public function getCst()
{
return $this->cst;
}
public function getSer()
{
return $this->ser;
}
public function getImp()
{
return $this->imp;
}
public function setCst($value)
{
$this->cst = $value;
}
public function setSer($value)
{
$this->ser = $value;
}
public function setImp($value)
{
$this->imp = $value;
}
}
$myObject = new ClientBody('toto', 'titi', 'tata');
echo $myObject->getCst(); // output 'toto'
echo $myObject->getSer(); // output 'titi'
echo $myObject->getImp(); // output 'tata'
Or you could use json_decode($client_body, TRUE);

Maintain Element in PHP Array And Update in PHP Class

I have one PHP class as below (part of the code):
class myclass{
private static $arrX = array();
private function is_val_exists($needle, $haystack) {
if(in_array($needle, $haystack)) {
return true;
}
foreach($haystack as $element) {
if(is_array($element) && $this->is_val_exists($needle, $element))
return true;
}
return false;
}
//the $anInput is a string e.g. Michael,18
public function doProcess($anInput){
$det = explode(",", $anInput);
if( $this->is_val_exists( $det[0], $this->returnProcess() ) ){
//update age of Michael
}
else{
array_push(self::$arrX, array(
'name' => $det[0],
'age' => $det[1]
));
}
}
public function returnProcess(){
return self::$arrX;
}
}
The calling code in index.php
$msg = 'Michael,18';
myclass::getHandle()->doProcess($msg);
In my webpage says index.php, it calls function doProcess() over and over again. When the function is called, string is passed and stored in an array. In the next call, if let's say same name is passed again, I want to update his age. My problem is I don't know how to check if the array $arrX contains the name. From my own finding, the array seems to be re-initiated (back to zero element) when the code is called. My code never does the update and always go to the array_push part. Hope somebody can give some thoughts on this. Thank you.
There is a ) missing in your else condition of your doProcess() function, it should read:
else{
array_push(self::$arrX, array(
'name' => $det[0],
'age' => $det[1]
)); // <-- there was the missing )
}
Here is a complete running solution based on your code:
<?php
class myclass{
private static $arrX = array();
private function is_val_exists($needle, $haystack) {
if(in_array($needle, $haystack)) {
return true;
}
foreach($haystack as $element) {
if(is_array($element) && $this->is_val_exists($needle, $element))
return true;
}
return false;
}
//the $anInput is a string e.g. Michael,18
public function doProcess($anInput){
$det = explode(",", $anInput);
if( $this->is_val_exists( $det[0], $this->returnProcess() ) ){
//update age of Michael
for ($i=0; $i<count(self::$arrX); $i++) {
if (is_array(self::$arrX[$i]) && self::$arrX[$i]['name'] == $det[0]) {
self::$arrX[$i]['age'] = $det[1];
break;
}
}
} else{
array_push(self::$arrX, array(
'name' => $det[0],
'age' => $det[1]
));
}
}
public function returnProcess(){
return self::$arrX;
}
}
$mc = new myclass();
$mc->doProcess('Michael,18');
$mc->doProcess('John,23');
$mc->doProcess('Michael,19');
$mc->doProcess('John,25');
print_r($mc->returnProcess());
?>
You can test it here: PHP Runnable
As I said in comments, it looks like you want to maintain state between requests. You can't use pure PHP to do that, you should use an external storage solution instead. If it's available, try Redis, it has what you need and is quite simple to use. Or, if you're familiar with SQL, you could go with MySQL for example.
On a side note, you should read more about how PHP arrays work.
Instead of array_push, you could have just used self::$arrX[] = ...
Instead of that, you could have used an associative array, e.g. self::$arrX[$det[0]] = $det[1];, that would make lookup much easier (array_key_exists etc.)
Can you try updating the is_val_exists as follows:
private function is_val_exists($needle, $haystack) {
foreach($haystack as $element) {
if ($element['name'] == $needle) {
return true;
}
return false;
}

PHP Magic methods to use property as array or string

I have some property $abc (type array) in object:
$this->abc = array(
'id' => 123,
'status' => 'close'
);
There is some solution to using this property as array or string? Like that:
echo $this->abc; // return first element of array: 123
echo $this->abc['status']; // return element by key: close
Maybe getter and setter or Reflection?
EDIT:
I prepare some like this, but all returns id value:
class Test {
private $abc;
public function __construct() {
$this->abc = array(
'id' => '123',
'status' => 'close'
);
}
public function __get($key) {
if ($key === 'abc') {
echo $this->abc['id'];
}
}
}
$t = new Test();
echo $t->abc['id']; // return 123 - correct!
echo $t->abc['status']; // return 123 - incorrect, should be return 'close' string
echo $t->abc; // return 123 - correct
Any suggestion?
Actually you can't do exactly what your example shows.
Magic methods (what a silly name, btw) simply allow you to get and set properties, so that some_object->a can be handled with custom code, but you cannot tell PHP to handle
some_object->a and some_object->a[0] differently.
You are free to have your custom getter return an int, an array or an elephant, but that's it.
EDIT: Your code does nothing but print abc['id'] each time the property is referenced.
abc is still handled as any plain old property.
Let's replace echo with return
public function __get($key) {
if ($key === 'abc') {
return$this->abc['id'];
}
}
Now whenever you reference abc, the getter gives you "123".
Demonstration:
echo $t->abc['id']; // 'id' evaluates to 0, so result is "1" ("123"[0])
echo $t->abc['status']; // same thing
echo $t->abc; // "123" - correct (of sorts)
echo $t->abc[1]; // "2" (2nd character of "123")
You are also free do do stupid things like that:
class fairytale {
private static $handsome =
array ("Prince Valliant", "Superman", "Flash Gordon");
private static $ugly =
array ("Michael Moore", "Condoleezza Rice", "Ronald McDonald");
function __get ($prop)
{
if ($prop=='frog')
return self::$handsome;
if (preg_match ('/^frog\\[([0-9]*)\\]$/', $prop, $res))
return self::$ugly[$res[1]];
}
}
function kiss ($prince)
{
echo "$prince appears in a puff of smoke...\n";
}
$pond = new fairytale();
$frog1 = $pond->frog[0]; // <-- array subscript parsed before getter is called
$frog2 = 'frog[0]';
$frog2 = $pond->$frog2; // <-- array subscript parsed inside getter
kiss ($frog1);
kiss ($frog2); // <--- surprise!
As a side note, abusing custom getters/setters to turn frogs into princes might be a lot of fun and a fine display of PHP expertise, but it's unlikely to produce readable and maintainable code.
Just my opinion, of course.
If you want to process the value when fetch or set the value, you can use __get / __set
You can use magic methods __set and __get, for example
class someClass{
private $_arr = array();
public function __set($key, $val){
if(!array_key_exists($key, $this->_arr)){
$this->_arr[$key] = array();
}
$this->_arr[$key] = array_merge($this->_arr[$key], (array)$val);
}
public function &__get($key) {
return $this->_arr[$key];
}
}
$obj = new someClass();
$obj->setvalue = array(5,6,7);
$obj->setvalue = 4;
You could also just do
reset($this->abc);
for the first element.

Categories