php arrays with only one type - php

I'm looking to create an array or list with elements of a certain type (eg objects the implement a certain interface). I know I can create an object that does the same thing implementing Traversable and Iterator, or override ArrayObject. But maybe there's another way I have missed.

Do you mean something like:
$array=Array();
foreach ($itemsToAdd as $item) {
if ($item instanceof NameOfwantedInterface) {
Array_push($array,$item);
}
}
If you don't, them I'm sorry - it's just that your question isn't too clear.

I would write a custom class that extended ArrayObject and threw an exception if you tried to assign a variable that wasn't the correct type, there's really no better way to do it that I can think of.

PHP as a lanugage is very flexible in terms of type handling and type conversion. You will probably have to put a manual check in if you want any kind of strong type checking, a simple if statement will do.
The array object is designed to be especially flexible (lazy key assignment, automatic increment, string or integer keys, etc.) so you should probably use a custom object of your own.

You could use type hinting:
<?php
interface Shape
{
function draw();
}
class MyArray
{
private $array = array();
function addValue(Shape $shape) //The hinting happens here
{
$array[] = $shape;
}
}
?>
This example is not perfect, but you'll get the idea.

Basically, you are going to want to do a function that checks if the variable you are inserting into the array is an object.
function add($var)
{
if(is_object($var))
{
$this->array[] = $var;
}
}
If you want to make sure it has a specific class name, you would do something like this (for PHP5):
function add(className $var)
{
$this->array[] = $var;
}
or this for previous PHP versions:
function add($var)
{
if($var instanceOf className)
{
$this->array[] = $var
}
}
You might want to look into array_filter() to do this without building an object.
Looking at that page, I've found that you can use array_filter with common functions like is_object. So doing something like this:
$this->array = array_filter($this->array ,'is_object');
Would filter the array to contain only objects.

Related

PHP: Documentation of function list

I have a file which consist of user defined functions with CamelCase names :
// some useful comments
function functionOne(){
return false;
}
// some other useful comment
function addTwoNumbers($x,$y)
{
return 5;
}
...
I would like to output these functions sorted by name like
addTwoNumbers($x,$y)
2 parameters
Comment: some other useful comment
functionOne()
0 parameters
Comment: some useful comments
So far, I get a list of function names like this:
include '../includes/functions.php';
$allFunctions = get_defined_functions();
$userFunctions = $allFunctions['user'];
sort($userFunctions);
foreach ($userFunctions as $functionName) {
echo $functionName. "<br>";
}
My main problem is that I do not know how to display the number of parameters of each function and the parameter variable names.
Further (but I could live with it) the function names are only displayed with lower case letters, so I can't read them in CamelCase.
And finally the comments are of course not shown. I was thinking of writing them in an array $comments['functionOne']="some useful comments".
So my main problem is how do you get the parameter variable names and number.
To get paramater number and names, you can use Reflection in PHP :
function getFunctionArgumentNames($functionName) {
$reflection = new ReflectionFunction($functionName);
$argumentNames= array();
foreach ($reflection ->getParameters() as $parameter) {
$argumentNames[] = $parameter->name;
}
return $argumentNames;
}
Then you'll have your parameter names, and the length of the returned array will give you how many there are.
More information can be found about it :
ReflectionClass
ReflectionObject
What is Reflection in PHP ?
Another common use of Reflection is for creating documentation. It would be extremely labour intensive to write documentation about every method of every class of a large framework or application. Instead, Reflection can automatically generate the documentation for you. It does this by inspecting each method, constructor and class to determine what goes in and what comes out.
Use ReflectionFunction class
for example:
$refFunc = new ReflectionFunction('preg_replace');
foreach( $refFunc->getParameters() as $param ){
print $param;
}
The ReflectionFunction class reports information about a function.
See more http://php.net/manual/en/class.reflectionfunction.php

Is there equivalent for __toString() method for other simple types in PHP?

_toString() is called when an object is used as string. How can I do something similar for numerical values, something like __toInt(), or __toArray(). Do such methods exist? Is there a work around? Is it a bad idea to use something like that even if there is a workaround for it?
There is no __toArray magic-method (just check the ones that exist here), but then, there shouldn't be, IMO.
Though people have asked for a magic toArray method, it doesn't look like such a method will be implemented any time soon.
Considering what objects are for, and how we use them, a toInt method wouldn't make much sense, and since all objects can be cast to an array, and can be iterated over, I see very little point in using __toArray anyway.
To "convert" on object to an array, you can use either one of the following methods:
$obj = new stdClass;
$obj->foo = 'bar';
var_dump((array) $obj);
//or
var_dump(json_decode(json_encode($obj), true));
This can be done with both custom objects, as stdClass instances alike.
As far as accessing them as an array, I can't see the point. Why write a slow magic method to be able to do something like:
$bar = 'foo';
$obj[$bar];
if you can do:
$obj->{$bar}
or if you can do:
foreach($obj as $property => $value){}
Or, if you need something a tad more specific, just implement any of the Traversable interfaces.
And for those rare cases, where you want an object to produce an array from specific properties in a very particular way, just write a method for that and call that method explicitly.
class ComplexObject
{
private $secret = null;
private $childObject = null;
public $foo = null;
//some methods, then:
public function toArray()
{//custom array representation of object
$data = array();
foreach($this->childObject as $property => $val)
{
if (!is_object($this->childObject->{$property}))
{
$data[$property] = $val;
}
}
$data['foo'] = $this->foo;
return $data;
}
//and even:
public function toJson()
{
return json_encode($this->toArray());
}
}
Ok, you have to call these methods yourself, explicitly, but that's not that hard, really... is it?

How to solve the missing object properties in PHP?

This is a bit philosophical but I think many people encountered this problem. The goal is to access various (dynamically declared) properties in PHP and get rid of notices when they are not set.
Why not to __get?
That's good option if you can declare your own class, but not in case of stdClass, SimpleXML or similar. Extending them is not and option since you usually do not instantiate these classes directly, they are returned as a result of JSON/XML parsing.
Example:
$data = '{"name": "Pavel", "job": "programmer"}';
$object = json_decode($data);
We have simple stdClass object. The problems is obvious:
$b = $data->birthday;
The property is not defined and therefore a notice is raised:
PHP Notice: Undefined property: stdClass::$birthday
This can happen very often if you consider that you get that object from parsing some JSON. The naive solution is obvious:
$b = isset($data->birthday) ? $data->birthday : null;
However, one gets tired very soon when wrapping every accessor into this. Especially when chaining the objects, such as $data->people[0]->birthday->year. Check whether people is set. Check if the first element is set. Check if birthday is set. Check if year is set. I feel a bit overchecked...
Question:
Finally, my question is here.
What is the best approach to this issue? Silencing notices does not seem to be the best idea. And checking every property is difficult. I have seen some solutions such as Symfony property access but I think it is still too much boilerplate. Is there any simpler way? Either third party library, PHP setting, C extension, I don't care as far as it works... And what are the possible pitfalls?
If I understand correctly, you want to deal with 3rd party Objects, where you have no control, but your logic requires certain properties that may not be present on the Object. That means, the data you accepting are invalid (or should be declared invalid) for your logic. Then the burden of checking the validity goes into your validator. Which I hope you already have following best practices to deal with 3rd party data. :)
You can use your own validator or one by frameworks. A common way is to write a set of Rules that your data needs to obey in order to be valid.
Now inside your validator, whenever a rule is not obeyed, you throw an Exception describing the error and attaching Exception properties that carry the information you want to use. Later when you call your validator somewhere in your logic, you place it inside try {...} block and you catch() your Exceptions and deal with them, that is, write your special logic reserved for those exceptions. As general practice, if your logic becomes too large in a block, you want to "outsource" it as function.
Quoting the great book by Robert Martin "Clean Code", highly recommended for any developer:
The first rule of function is that they should be small. The second is that they should be smaller than that.
I understand your frustration dealing with eternal issets and see as cause of the problem here that each time you need to write a handler dealing with that technical issue of this or that property not present. That technical issue is of very low level in your abstraction hierarchy, and in order to handle it properly, you have to go all the way up your abstraction chain to reach a higher step that has a meaning for your logic. It is always hard to jump between different levels of abstraction, especially far apart. It is also what makes your code hard to maintain and is recommended to avoid.
Ideally your whole architecture is designed as a tree where Controllers sitting at its nodes only know about the edges going down from them.
For instance, coming back to your example, the question is -
Q - What is the meaning for your app of the situation that $data->birthday is missing?
The meaning will depend on what the current function throwing the Exception wants to achieve. That is a convenient place to handle your Exception.
Hope it helps :)
One solution (I don't know if it's the better solution, but one possible solution) is to create a function like this:
function from_obj(&$type,$default = "") {
return isset($type)? $type : $default;
}
then
$data = '{"name": "Pavel", "job": "programmer"}';
$object = json_decode($data);
$name = from_obj( $object->name , "unknown");
$job = from_obj( $object->job , "unknown");
$skill = from_obj( $object->skills[0] , "unknown");
$skills = from_obj( $object->skills , Array());
echo "Your name is $name. You are a $job and your main skill is $skill";
if(count($skills) > 0 ) {
echo "\n\nYour skills: " . implode(",",$skills);
}
I think it's convienent because you have at the top of your script what you want and what it should be (array, string, etc)
EDIT:
Another solution. You could create a Bridge class that extends ArrayObject:
class ObjectBridge extends ArrayObject{
private $obj;
public function __construct(&$obj) {
$this->obj = $obj;
}
public function __get($a) {
if(isset($this->obj->$a)) {
return $this->obj->$a;
}else {
// return an empty object in order to prevent errors with chain call
$tmp = new stdClass();
return new ObjectBridge($tmp);
}
}
public function __set($key,$value) {
$this->obj->$key = $value;
}
public function __call($method,$args) {
call_user_func_array(Array($this->obj,$method),$args);
}
public function __toString() {
return "";
}
}
$data = '{"name": "Pavel", "job": "programmer"}';
$object = json_decode($data);
$bridge = new ObjectBridge($object);
echo "My name is {$bridge->name}, I have " . count($bridge->skills). " skills and {$bridge->donald->duck->is->paperinik}<br/>";
// output: My name is Pavel, I have 0 skills and
// (no notice, no warning)
// we can set a property
$bridge->skills = Array('php','javascript');
// output: My name is Pavel, my main skill is php
echo "My name is {$bridge->name}, my main skill is {$bridge->skills[0]}<br/>";
// available also on original object
echo $object->skills[0]; // output: php
Personally I would prefer the first solution. It's more clear and more safe.
Data formats which have optional fields are quite difficult to deal with. They're problematic in particular if you have third parties accessing or providing the data, since there rarely is enough documentation to comprehensively cover all causes for the fields to appear or disappear. And of course, the permutations tend to be harder to test, because coders won't instinctively realize that the fields may be there.
That's a long way of saying that if you can avoid having optional fields in your data, the best approach to dealing with missing object properties in PHP is to not have any missing object properties...
If the data you're dealing with is not up to you, then I'd look into forcing default values on all fields, perhaps via a helper function or some sort of crazy variation of the prototype pattern. You could build a data template, which contains default values for all fields of the data, and merge that with the real data.
However, if you do that, are you failing, unless? (Which is another programming philosophy to take into heart.) I suppose one could make the case that providing safe default parameters satisfies data validation for any missing fields. But particularly when dealing with third party data, you should exercise high level of paranoia against any field you're plastering with default values. It's too easy to just set it to null and -- in the process -- fail to understand why it was missing in the first place.
You should also ask what are you trying to achieve? Clarity? Safety? Stability? Minimal code duplication? These are all valid goals. Being tired? Less so. It suggests a lack disciprine, and a good programmer is always disciprined. Of course, I'll accept that people are less likely to do something, if they view it as a chore.
My point is, the answer to your question may differ depending on why it's being asked. Zero effort solution is probably not going to be available, so if you're only exchanging one menial programming task to another one, are you solving anything?
If you are looking for a systematic solution that will guarantee that the data is always in the format you have specified, leading to reduced number of logical tests in the code that processes that data, then perhaps what I suggested above will be of help. But it will not come without a cost and effort.
in PHP version 8
you can use Nullsafe operator as follow:
$res = $data?->people[0]?->birthday?->year;
The best answers have been given, but here is a lazy one:
$data = '{"name": "Pavel", "job": "programmer"}';
$object = json_decode($data);
if(
//...check mandatory properties: !isset($object->...)&&
){
//error
}
error_reporting(E_ALL^E_NOTICE);//Yes you're right, not the best idea...
$b = $object->birthday?:'0000-00-00';//thanks Elvis (php>5.3)
//Notice that if your default value is "null", you can just do $b = $object->birthday;
//assign other vars here
error_reporting(E_ALL);
//Your code
Use a Proxy object - it will add just one tiny class and one line per object instantiation to use it.
class ProxyObj {
protected $obj;
public function __construct( $obj ) {
$this->_obj = $obj;
}
public function __get($key) {
if (isset($this->_obj->$key)) {
return $this->_obj->$key;
}
return null;
}
public function __set($key, $value) {
$this->_obj->$key = $value;
}
}
$proxy = new ProxyObj(json_decode($data));
$b = $proxy->birthday;
You can decode the JSON object to an array:
$data = '{"name": "Pavel", "job": "programmer"}';
$jsonarray = json_decode($data, true);
$b = $jsonarray["birthday"]; // NULL
function check($temp=null) {
if(isset($temp))
return $temp;
else
return null;
}
$b = check($data->birthday);
I've hit this problem, mainly from getting json data from a nosql backed api that by design has inconsistent structures, eg if a user has an address you'll get $user->address otherwise the address key just isn't there. Rather than put tons of issets in my templates I wrote this class...
class GracefulData
{
private $_path;
public function __construct($d=null,$p='')
{
$this->_path=$p;
if($d){
foreach(get_object_vars($d) as $property => $value) {
if(is_object($d->$property)){
$this->$property = new GracefulData($d->$property,$this->_path . '->' . $property);
}else{
$this->$property = $value;
}
}
}
}
public function __get($property) {
return new GracefulData(null,$this->_path . '->' . $property);
}
public function __toString() {
Log::info('GracefulData: Invalid property accessed' . $this->_path);
return '';
}
}
and then instantiate it like so
$user = new GracefulData($response->body);
It will gracefully handle nested calls to existing and non existing properties. What it can't handle though is if you access a child of an existing non-object property eg
$user->firstName->something
Lots of good answers here, I consider #Luca 's answer as one of the best - I extended his a little so that I could pass in either an array or object and have it create an easy to use object. Here's mine:
<?php
namespace App\Libraries;
use ArrayObject;
use stdClass;
class SoftObject extends ArrayObject{
private $obj;
public function __construct($data) {
if(is_object($data)){
$this->obj = $data;
}elseif(is_array($data)){
// turn it into a multidimensional object
$this->obj = json_decode(json_encode($data), false);
}
}
public function __get($a) {
if(isset($this->obj->$a)) {
return $this->obj->$a;
}else {
// return an empty object in order to prevent errors with chain call
$tmp = new stdClass();
return new SoftObject($tmp);
}
}
public function __set($key, $value) {
$this->obj->$key = $value;
}
public function __call($method, $args) {
call_user_func_array(Array($this->obj,$method),$args);
}
public function __toString() {
return "";
}
}
// attributions: https://stackoverflow.com/questions/18361594/how-to-solve-the-missing-object-properties-in-php | Luca Rainone
I have written a helper function for multilevel chaining, for example, let's say you want to do something like $obj1->obj2->obj3->obj4, and my helper will return empty string if one of the tiers is not defined or null
class MyUtils
{
// for $obj1->obj2->obj3: MyUtils::nested($obj1, 'obj2', 'obj3')
// returns '' if some of tiers is null
public static function nested($obj1, ...$tiers)
{
if (!isset($obj1)) return '';
$a = $obj1;
for($i = 0; $i < count($tiers); $i++){
if (isset($a->{$tiers[$i]})) {
$a = $a->{$tiers[$i]};
} else {
return '';
}
}
return $a;
}
}

instanceof equivalent for a string-represented classname

I'm looking for a functionality, in the example below called "theFunctionILookFor", that will make the code work.
$myClassName = "someName";
$parentOrInterfaceName = "someParent";
if (theFunctionILookFor($myClassName)) {
echo "is parent";
}
Edit: I try to avoid instantiating the class just to perform this check. I would like to be able to pass 2 string parameters to the checking function
Looks like this is my answer: is_subclass_of
It can accept both parameters as strings
http://php.net/manual/en/function.is-subclass-of.php
Try is_subclass_of(). It can accept both parameters as strings. http://php.net/manual/en/function.is-subclass-of.php
Using the Reflection API, you can construct a ReflectionClass object using the name of the class as well as an object.
You might use this?
http://www.php.net/manual/en/function.get-parent-class.php
From the page:
get_parent_class
(PHP 4, PHP 5, PHP 7)
get_parent_class — Retrieves the parent class
name for object or class
Description
string get_parent_class ([ mixed $object ] )
Retrieves the parent
class name for object or class.
This is an old thread, but still for future reference, you can use the functions is_a and is_subclass_of to accomplish this in a more efficient way.
No need to instantiate anything, you can just pass your string like:
if (is_subclass_of($myObject, 'SomeClass')) // is_a(...)
echo "yes, \$myObject is a subclass of SomeClass\n";
else
echo "no, \$myObject is not a subclass of SomeClass\n";
PHP Docs:
http://www.php.net/manual/en/function.is-a.php
http://www.php.net/manual/en/function.is-subclass-of.php
I think that instanceof is a lot faster and more clear to read.
instanceof does not work on strings, because it can be used as a "identifier" for a class:
$temp = 'tester';
$object = new sub();
var_dump($object instanceof $temp); //prints "true" on php 5.3.8
class tester{}
class sub extends \tester{
}
Also, it works on interfaces - you just need a variable to contain the absolute class name (including namespace, if you want to be 100% sure).
Maybe it's old question, but problem still is actual, here, below, the best solution I've found
class ProductAttributeCaster
{
public function cast(mixed $value): mixed
{
if ($value instanceof ($this->castTo())) {
// do stuff
}
return $value;
}
function castTo(): string
{
return ProductAttribute::class;
}
}

PHP: Passing functions to a class

I made a class. I give it some objects (mostly retreived database rows) as input, and tell it which fields it has to show, and which buttons I want. Then it renders a very nice html table! It's pretty awesome, I think.
$ot = new ObjectTable();
$ot->objects = $some_objects;
$ot->fields = array('id','name','description','image');
$ot->buttons = array('edit','delete');
$ot->render();
However, I also want to be able to manipulate the data it shows. For example, i want to be able to truncate the 'description' field. Or to display an image thumbnail (instead of 'picture.jpg' as text). I don't know how to pass these functions to the class. Perhaps something like:
$ot->functions = array(null,null,'truncate','thumbnail');
But then I don't know how to convert these strings to run some actual code, or how to pass parameters.
There must be a nice way to do what I want. How?
Check this question and the answer is:
As of PHP5.3 you could use closures
or functors to pass methods
around. Prior to that, you could write
an anonymous function with
create_function(), but that is
rather awkward.
But what you are trying to achieve is best solved by passing Filter Objects to your renderer though. All filters should use the same method, so you can use it in a Strategy Pattern way, e.g. write an interface first:
interface Filter
{
public function filter($value);
}
Then write your filters implementing the interface
class TruncateFilter implements Filter
{
protected $_maxLength;
public function __construct($maxLength = 50)
{
$this->_maxLength = (int) $maxLength;
}
public function filter($value)
{
return substr(0, $this->_maxLength, $value) . '…';
}
}
Give your ObjectTable a method to accept filters
public function addFilter($field, Filter $filter)
{
if(in_array($field, $this->fields)) {
$this->_filters[$field][] = $filter;
}
return $this;
}
And when you do your ObjectTable instantiation, use
$ot = new ObjectTable();
$ot->objects = $some_objects;
$ot->fields = array('id','name','description','image');
$ot->addFilter('description', new TruncateFilter)
->addFilter('name', new TruncateFilter(10))
->addFilter('image', new ThumbnailFilter);
Then modify your render() method to check if there is any Filters set for the fields you are rendering and call the filter() method on them.
public function render()
{
foreach($this->fields as $field) {
$fieldValue = // get field value somehow
if(isset($this->filters[$field])) {
foreach($this->filters[$field] as $filter) {
$fieldValue = $filter->filter($fieldValue)
}
}
// render filtered value
}
}
This way you can add infinite filters.
PHP has a pseudo-type called "callback", which is actually an ugly closure in disguise. You can call such callbacks using call_user_func() or call_user_func_array():
$callback = 'strlen';
echo call_user_func($callback, '123');
// will output 3 (unless you're using a strange encoding)
You are looking for create_function().
However, creating functions on runtime and adding them to a class doesn't sound right to me. It's likely to become a maintenance nightmare very quickly. There are better ways to achieve what you want. What kind of manipulation would the functions to to the data? How about storing them in a "tools" class and connecting that with the table object when needed?
$functions = array(null,null,'truncate','thumbnail');
$function_1 = $functions[3];
$my_string = 'string to truncate';
$result = call_user_func($functions[2], $my_string);
If you want to pass multiple parameters, use call_user_func_array instead.
You might also want to explore call_user_func, which allows you to call a function based on a string representing its name.

Categories