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);
Related
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.
Is it possible to set multiple properties at a time for an object in php?
Instead of doing:
$object->prop1 = $something;
$object->prop2 = $otherthing;
$object->prop3 = $morethings;
do something like:
$object = (object) array(
'prop1' => $something,
'prop2' => $otherthing,
'prop3' => $morethings
);
but without overwriting the object.
Not like the way you want. but this can be done by using a loop.
$map = array(
'prop1' => $something,
'prop2' => $otherthing,
'prop3' => $morethings
);
foreach($map as $k => $v)
$object->$k = $v;
See only 2 extra lines.
You should look at Object Oriented PHP Best Practices :
"since the setter functions return $this you can chain them like so:"
$object->setName('Bob')
->setHairColor('green')
->setAddress('someplace');
This incidentally is known as a fluent interface.
I would recommend you don't do it. Seriously, don't.
Your code is much MUCH cleaner the first way, it's clearer of your intentions, and you aren't obfocusing your code to the extent where sometime in the future someone would look at your code and think "What the hell was the idiot thinking"?
If you insist on doing something which is clearly the wrong way to go, you can always create an array, iterate it and set all the properties in a loop. I won't give you code though. It's evil.
You could write some setters for the object that return the object:
public function setSomething($something)
{
$this->something = $something;
return $this; //this will return the current object
}
You could then do:
$object->setSomething("something")
->setSomethingelse("somethingelse")
->setMoreThings("some more things");
You would need to write a setter for each property as a __set function is not capable of returning a value.
Alternatively, set a single function to accept an array of property => values and set everything?
public function setProperties($array)
{
foreach($array as $property => $value)
{
$this->{$property} = $value;
}
return $this;
}
and pass in the array:
$object->setProperties(array('something' => 'someText', 'somethingElse' => 'more text', 'moreThings'=>'a lot more text'));
I realise this is an old question but for the benefit of others that come across it, I solved this myself recently and wanted to share the result
<?php
//Just some setup
header('Content-Type: text/plain');
$account = (object) array(
'email' => 'foo',
'dob'=>((object)array(
'day'=>1,
'month'=>1,
'year'=>((object)array('century'=>1900,'decade'=>0))
))
);
var_dump($account);
echo "\n\n==============\n\n";
//The functions
function &getObjRef(&$obj,$prop) {
return $obj->{$prop};
}
function updateObjFromArray(&$obj,$array){
foreach ($array as $key=>$value) {
if(!is_array($value))
$obj->{$key} = $value;
else{
$ref = getObjRef($obj,$key);
updateObjFromArray($ref,$value);
}
}
}
//Test
updateObjFromArray($account,array(
'id' => '123',
'email' => 'user#domain.com',
'dob'=>array(
'day'=>19,
'month'=>11,
'year'=>array('century'=>1900,'decade'=>80)
)
));
var_dump($account);
Obviously there are no safeguards built in. The main caveat is that the updateObjFromArray function assumes that for any nested arrays within $array, the corresponding key in $obj already exists and is an object, this must be true or treating it like an object will throw an error.
Hope this helps! :)
I wouldn't actually do this....but for fun I would
$object = (object) ($props + (array) $object);
you end up with an stdClass composed of $objects public properties, so it loses its type.
Method objectThis() to transtypage class array properties or array to stdClass. Using direct transtypage (object) would remove numeric index, but using this method it will keep the numeric index.
public function objectThis($array = null) {
if (!$array) {
foreach ($this as $property_name => $property_values) {
if (is_array($property_values) && !empty($property_values)) {
$this->{$property_name} = $this->objectThis($property_values);
} else if (is_array($property_values) && empty($property_values)) {
$this->{$property_name} = new stdClass();
}
}
} else {
$object = new stdClass();
foreach ($array as $index => $values) {
if (is_array($values) && empty($values)) {
$object->{$index} = new stdClass();
} else if (is_array($values)) {
$object->{$index} = $this->objectThis($values);
} else if (is_object($values)) {
$object->{$index} = $this->objectThis($values);
} else {
$object->{$index} = $values;
}
}
return $object;
}
}
Is there a way to instantiate a new PHP object in a similar manner to those in jQuery? I'm talking about assigning a variable number of arguments when creating the object. For example, I know I could do something like:
...
//in my Class
__contruct($name, $height, $eye_colour, $car, $password) {
...
}
$p1 = new person("bob", "5'9", "Blue", "toyota", "password");
But I'd like to set only some of them maybe. So something like:
$p1 = new person({
name: "bob",
eyes: "blue"});
Which is more along the lines of how it is done in jQuery and other frameworks. Is this built in to PHP? Is there a way to do it? Or a reason I should avoid it?
the best method to do this is using an array:
class Sample
{
private $first = "default";
private $second = "default";
private $third = "default";
function __construct($params = array())
{
foreach($params as $key => $value)
{
if(isset($this->$key))
{
$this->$key = $value; //Update
}
}
}
}
And then construct with an array
$data = array(
'first' => "hello"
//Etc
);
$Object = new Sample($data);
class foo {
function __construct($args) {
foreach($args as $k => $v) $this->$k = $v;
echo $this->name;
}
}
new foo(array(
'name' => 'John'
));
The closest I could think of.
If you want to be more fancy and just want to allow certain keys, you can use __set() (only on php 5)
var $allowedKeys = array('name', 'age', 'hobby');
public function __set($k, $v) {
if(in_array($k, $this->allowedKeys)) {
$this->$k = $v;
}
}
get args won't work as PHP will see only one argument being passed.
public __contruct($options) {
$options = json_decode( $options );
....
// list of properties with ternary operator to set default values if not in $options
....
}
have a looksee at json_decode()
The closest I can think of is to use array() and extract().
...
//in your Class
__contruct($options = array()) {
// default values
$password = 'password';
$name = 'Untitled 1';
$eyes = '#353433';
// extract the options
extract ($options);
// stuff
...
}
And when creating it.
$p1 = new person(array(
'name' => "bob",
'eyes' => "blue"
));
I am trying to create an form validator class that will check to make sure that people have typed a particular number of characters into specified fields. Here is my page:
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
require_once('functions.php');
require_once('formValidator.php');
$validator = new FormValidator();
$validator->setFirst($_POST['first']);
$validator->setLast($_POST['last']);
$validator->setEmail($_POST['email']);
$errors = array();
$validator->checkLength($errors);
echo $errors;
if (!empty($errors)) {
echo '<p>' . $message . '</p>';
}
}
// form here (code above is in PHP tags, of course)
Here is my class:
class FormValidator {
public $first;
public $last;
public $email;
public $fields_with_lengths;
public function setFirst($first) {
$this->first = $first;
}
public function setLast($last) {
$this->last = $last;
}
public function setEmail($email) {
$this->email = $email;
}
function setLengths() {
$this->fields_with_lengths = array('first' => 2, 'last' => 2, 'email' => 8);
}
function checkLength($fields_with_lengths) {
$length_errors = array();
foreach($fields_with_lengths as $fieldname => $maxlength) {
if (strlen(trim($_POST[$fieldname])) > $maxlength) {
$length_errors[] = $fieldname;
}
}
if (!empty($length_errors)) {
$message = 'There were no errors';
} else {
$message = 'There were errors';
}
return $message;
}
}
$validator = new FormValidator();
$validator->setFirst($_POST['first']);
$validator->setLast($_POST['last']);
$validator->setEmail($_POST['email']);
Can anyone see what I am doing wrong? I'd be grateful for some help. Thanks.
You're passing an empty array into checkLength(), then iterating over it (so the for loop will never have anything to iterate over). You're returning $message from checkLength() but not assigning it to anything.
$errors = array('first' => 8,
'last' => 8,
'email' => 16
);
$message = $validator->checkLength($errors);
In PHP by default all values to functions are passed as values, not references (unless they are objects).
First of all, if you want to use a variable as a return value, you'd have to define checkLength($var) as:
function checkLength(&$fields_with_lengths) { /* ... */ }
You could in PHP4 do a call like checkLength(&$errors), but that's deprecated as of PHP5 and will throw an E_STRICT warning. Of course, you still can (but that functionality may be thrown away soon), though the "correct" way is, as said, giving it as reference.
An even more elegant way of acting, and what should really be done in PHP, is to return the $fields_with_lengths. This way:
function checkLength() {
$var = array();
// Your code
return $var;
}
And in the call to the function, just say
$return = $validator->checkLength();
But that does not solve the logic of your problem.
You are iterating over a parameter which is an empty array.
You are not iterating over $validator->fields_with_lengths.
Even more, $fields_with_lengths, in your code is not initialized ever.
You would have to call $validator->setLengths().
A better way of doing is (you are using PHP5) declaring a constructor, which in PHP5 is done with the magic function __construct().
However, as you are just initializing the variable always with the same constant values, you can do that in the very definition, just like this:
So you would have something like:
class FormValidator {
pubilc $first;
pubilc $last;
pubilc $email;
pubilc $fields_with_lengths = array('first' => 2, 'last' => 2, 'email' => 8);
// Rest of code
}
What's more..
¿Why are the attributes marked as public?
OOP theory tells us to close the scope of visibility as much as possible. And as you have setters, you don't need to access the attributes directly.
You can in PHP have a magic__get($var)to return a private $attribute if you want the syntactic sugar ofecho $validator->first;`.
Example:
function __get($var) {
if(isset($this->{$var})) return $this->{$var}; // this $this->{$var} could even be written as $this->$var but I did it like that for clarity.
}
If you also want the syntactic sugar of $validator->first = "John"; you can do the same with the setter, as in:
function __set($var, $value) {
if(isset($this->{$var})) {
return $this->{$var} = $value;
} return null; // Or you can throw an exception if you like
}
In PHP, as opposed to other languages, such as Java, you must explicitly use $this as the current instance of an object within a function.
So your checkLength is misdefined.
You should also do something with the return value, instead of just dropping it.
With just that minor modifications, you could fix your code with
function checkLength() {
$length_errors = array();
foreach($this->fields_with_lengths as $fieldname => $maxlength) {
if (strlen(trim($_POST[$fieldname])) > $maxlength) {
$length_errors[] = $fieldname;
}
}
if (!empty($length_errors)) {
$message = 'There were no errors';
} else {
$message = 'There were errors';
}
return array($length_errors, $message);
}
You were using a variable ($length_errors otherwise inaccessible! so you were computing data which would never be useful).
And in the call to the function, something like
$validator = new FormValidator();
// You could leave the setXXX($_POST[xxx])
$validator->first = $_POST['first'];
$validator->last = $_POST['last'];
$validator->email = ($_POST['email'];
$errors = $validator->checkLength();
echo "<p> {$errors[1]} </p>";
if(!empty($errors[0])) {
// You could do something like foreach($errors[0] as $k => $fieldname) { echo "Error in $fieldname<br />" }
}
$errors is empty because you initialize it but never change it. $message is unset because you never assign anything to it.
You're defining $errors = array();, making the errors an empty array. Nowhere do you attempt to set it to any other value, thus it remains an empty array.
I'm currently working on an OO PHP application. I have a class called validation which I would like to use to check all of the data submitted is valid, however I obviously need somewhere to define the rules for each property to be checked. At the moment, I'm using arrays during the construction of a new object. eg:
$this->name = array(
'maxlength' => 10,
'minlength' => 2,
'required' => true,
'value' => $namefromparameter
)
One array for each property.
I would then call a static method from the validation class which would carry out various checks depending on the values defined in each array.
Is there a more efficient way of doing this?
Any advice appreciated.
Thanks.
I know the associative array is used commonly to configure things in PHP (it's called magic container pattern and is considered bad practice, btw), but why don't you create multiple validator classes instead, each of which able to handle one rule? Something like this:
interface IValidator {
public function validate($value);
}
$validators[] = new StringLengthValidator(2, 10);
$validators[] = new NotNollValidator();
$validators[] = new UsernameDoesNotExistValidator();
This has multiple advantages over the implementation using arrays:
You can document them (very important), phpdoc cannot parse comments for array keys.
Your code becomes typo-safe (array('reqiured' => true))
It is fully OO and does not introduce new concepts
It is more readable (although much more verbose)
The implementation of each constraint can be found intuitively (it's not in a 400-line function, but in the proper class)
EDIT: Here is a link to an answer I gave to a different question, but that is mostly applicable to this one as well.
Since using OO it would be cleaner if you used classes for validating properties. E.g.
class StringProperty
{
public $maxLength;
public $minlength;
public $required;
public $value;
function __construct($value,$maxLength,$minLength,$required)
{
$this->value = $value;
$this-> maxLength = $maxLength;
$this-> minLength = $minLength;
$this-> required = $required;
}
function isValidat()
{
// Check if it is valid
}
function getValidationErrorMessage()
{
}
}
$this->name = new StringProperty($namefromparameter,10,2,true);
if(!$this->name->isValid())
{
$validationMessage = $this->name-getValidationErrorMessage();
}
Using a class has the advantage of encapsulating logic inside of it that the array (basically a structure) does not have.
Maybe get inspired by Zend-Framework Validation.
So define a master:
class BaseValidator {
protected $msgs = array();
protected $params = array();
abstract function isValid($value);
public function __CONSTRUCT($_params) {
$this->params = $_params;
}
public function getMessages() {
// returns errors-messages
return $this->msgs;
}
}
And then build your custom validators:
class EmailValidator extends BaseValidator {
public function isValid($val=null) {
// if no value set use the params['value']
if ($val==null) {
$val = $this->params['value'];
}
// validate the value
if (strlen($val) < $this->params['maxlength']) {
$this->msgs[] = 'Length too short';
}
return count($this->msgs) > 0 ? false : true;
}
}
Finally your inital array could become something like:
$this->name = new EmailValidator(
array(
'maxlength' => 10,
'minlength' => 2,
'required' => true,
'value' => $namefromparameter,
),
),
);
validation could then be done like this:
if ($this->name->isValid()) {
echo 'everything fine';
} else {
echo 'Error: '.implode('<br/>', $this->name->getMessages());
}