I have the following two classes (I have not included the interfaces)
ConditionsRefer
namespace PG\Referrer\Single\Post;
class ConditionsRefer implements ConditionsReferInterface
{
/**
* #var $authorReferrer = null
*/
private $isAuthorReferrer = null;
/**
* #var $dateReferrer = null
*/
private $isDateReferrer = null;
/**
* #var $searchReferrer = null
*/
private $isSearchReferrer = null;
/**
* #var $taxReferrer = null
*/
private $isTaxReferrer = null;
/**
* #param array $values = null;
*/
public function __construct(array $values = null)
{
if ($values)
$this->setBulk($values);
}
/**
* Bulk setter Let you set the variables via array or object
*/
public function setBulk($values)
{
global $wp_query;
if (!is_array($values) && !$values instanceof \stdClass) {
throw new \InvalidArgumentException(
sprintf(
'%s needs either an array, or an instance of \\stdClass to be passed, instead saw %s',
__METHOD__,
is_object($values) ? get_class($values) : gettype($values)
)
);
}
foreach ($values as $name => $value) {//create setter from $name
if (array_key_exists($value, $wp_query->query_vars)) { //Check that user don't set a reserved query vars
throw new \InvalidArgumentException(
sprintf(
'%s is a reserved query_vars and cannot be used. Please use a unique value',
$value
)
);
}
$setter = 'set' . $name;
$condition = isset($_GET[$value]);
if ($setter !== 'setBulk' && method_exists($this, $setter)) {
$this->{$setter}($condition);//set value (bool)
}
}
return $this;
}
/**
* #param $authorReferrer
* #return $this
*/
public function setAuthorReferrer($isAuthorReferrer)
{
$this->isAuthorReferrer = $isAuthorReferrer;
return $this;
}
/**
* #param $dateReferrer
* #return $this
*/
public function setDateReferrer($isDateReferrer)
{
$this->isDateReferrer = $isDateReferrer;
return $this;
}
/**
* #param $searchReferrer
* #return $this
*/
public function isSearchReferrer($isSearchReferrer)
{
$this->isSearchReferrer = $isSearchReferrer;
return $this;
}
/**
* #param $taxReferrer
* #return $this
*/
public function setTaxReferrer($isTaxReferrer)
{
$this->isTaxReferrer = $isTaxReferrer;
return $this;
}
}
QueryArgumentsRefer
namespace PG\Referrer\Single\Post;
class QueryArgumentsRefer implements QueryArgumentsReferInterface
{
private $referrer;
public function __construct(ConditionsReferInterface $referrer, array $values = array())
{
$this->referrer = $referrer;
$this->referrer->setBulk($values);
}
public function getReferrer()
{
return $this->referrer;
}
public function b()
{
$test = (object) $this->referrer;
if($test->isAuthorReferrer === false)
return 'This is just a test';
}
}
This is how I use it in a file
$a = new QueryArgumentsRefer(new ConditionsRefer(), ['authorReferrer' => 'aq']);
?><pre><?php var_dump($a->b()); ?></pre><?php
In function b() in class QueryArgumentsRefer, I need to use the properties of class ConditionsRefer.
This is the result of $test, which is the expected result, so this is working
object(PG\Referrer\Single\Post\ConditionsRefer)#522 (4) {
["isAuthorReferrer":"PG\Referrer\Single\Post\ConditionsRefer":private]=>
bool(false)
["isDateReferrer":"PG\Referrer\Single\Post\ConditionsRefer":private]=>
NULL
["isSearchReferrer":"PG\Referrer\Single\Post\ConditionsRefer":private]=>
NULL
["isTaxReferrer":"PG\Referrer\Single\Post\ConditionsRefer":private]=>
NULL
}
If I try to use $test->isAuthorReferrer, I get the following error
Fatal error: Cannot access private property PG\Referrer\Single\Post\ConditionsRefer::$isAuthorReferrer
which is expected I guess. The only way to make this work in my mind is setting the properties in ConditionsRefer to public
I've read properties should be private, and not public. How can I properly work around this problem, or do I have to make my properties public
EDIT
I have tried setting my properties to protected, but that does noet help as this also gives me a fatala error
Use protected and your child classes can use it. This way you can still have access to it in children classes without making it public. private means only the base class may use it.
Also, as a side note, all variables in a class without a default value will default to null
/**
* #var $authorReferrer = null
*/
protected $isAuthorReferrer;
Solve this issue. I'm still new to OOP and had a slight misunderstanding about setters and getter.
What I did is, I created a getter for each setter in the ConditionsRefer class, and instead of trying to use the properties of this class in the QueryArgumentsRefer class (which caused the initial error), I used the getters to get my info from the ConditionsRefer class inside the QueryArgumentsRefer class like
$this->conditionalReferrer->isAuthorReferrer();
Related
I wrote a dependency provider (basically IOC container) for my application. I added the feature of autowiring, although it hits a snag when the class it is trying to autowire doesn't have a constructor.
Uncaught Error: Call to a member function getParameters() on null in C:\xampp\htdocs\src\app\Providers\DependencyProvider.php:68
I am fairly certain it is trying to resolve constructor arguments in the resolveArguments method, on a class that doesn't have a constructor, and this is why the issue is happening.
So, resolveArguments should only be called if the class needs their arguments resolved (autowired), and that is usually only when it has a constructor.
The error message above, is happening because getConstructor is returning null. I am asking what is the best practice to check if a reflection class has a constructor that needs autowiring?
Full class:
<?php
namespace App\Providers;
class DependencyProvider {
private static $objects = [];
/**
* Register an instantiated object to the container.
*
* #param object $object
*/
public static function register(object $object) : void {
self::$objects[get_class($object)] = $object;
}
/**
* Fetch a cached object from the container.
*
* #param string $objectName
* #return object
*/
public static function fetch(string $objectName) : object {
if (array_key_exists($objectName, self::$objects)) {
return self::$objects[$objectName];
}
$object = self::make($objectName);
self::$objects[$objectName] = $object;
return $object;
}
/**
* Creates an object from its name and auto-wires constructor arguments.
*
* #param string $objectName
* #return object
* #throws \ReflectionException
*/
private static function make(string $objectName) : object {
$reflection = new \ReflectionClass($objectName);
if (!$reflection->isInstantiable()) {
throw new RuntimeException($reflection->getName() . ' can\'t be instantiated.');
}
$arguments = self::resolveArguments($reflection);
if (count($arguments) < 1) {
return $reflection->newInstance();
}
else {
return $reflection->newInstanceArgs($arguments);
}
}
/**
* Creates an array of arguments from a reflection class.
* Uses default value if there is one, auto-wires the object if not.
*
* #param $reflection
* #return array
*/
private static function resolveArguments($reflection) : array {
$constructor = $reflection->getConstructor();
$parameters = $constructor->getParameters();
if (!$parameters) {
return $reflection->newInstance();
}
$arguments = [];
foreach ($parameters as $parameter) {
if ($parameter->isDefaultValueAvailable()) {
$arguments[] = $parameter->getDefaultValue();
continue;
}
if ($parameter->getClass() == null) {
exit($parameter->name . ' on ' . $reflection->getName() . ' needs a default value');
}
$arguments[] = self::fetch($parameter->getClass()->getName());
}
return $arguments;
}
}
Edit (misread the question):
Just check that the constructor actually exists before calling that method:
if (! is_null($reflection->getConstructor())) { ... }
This question already has answers here:
PHP class instance to JSON
(5 answers)
Closed 4 years ago.
I got an object. I need to turn into JSON for storage but when I try to encode it into JSON it returns an empty JSON object. When I tried to use json_last_error.
The code I used
echo $payload["sub"];
echo json_encode($user);
echo json_last_error_msg();
The result I get
"102573480781696194937{}No error".
The User class I'm trying to encode
<?php
/**
* Created by PhpStorm.
* User: Student
* Date: 13-4-2018
* Time: 10:40
*/
namespace php;
class User
{
private $isAdmin = false;
private $registeredFood = array();
private $googleID;
private $name;
private $notes = array();
private $email;
/**
* User constructor.
* #param $googleID
*/
public function __construct($googleID)
{
$this->googleID = $googleID;
}
/**
* #return mixed
*/
public function getGoogleID()
{
return $this->googleID;
}
/**
* #return bool
*/
public function isAdmin()
{
return $this->isAdmin;
}
/**
* #param bool $isAdmin
*/
public function setIsAdmin($isAdmin)
{
$this->isAdmin = $isAdmin;
}
/**
* #return array
*/
public function getRegisteredFood()
{
return $this->registeredFood;
}
/**
* #param array $registeredFood
*/
public function setRegisteredFood($registeredFood)
{
$this->registeredFood = $registeredFood;
}
/**
* #return mixed
*/
public function getName()
{
return $this->name;
}
/**
* #param mixed $name
*/
public function setName($name)
{
$this->name = $name;
}
/**
* #return array
*/
public function getNotes()
{
return $this->notes;
}
/**
* #param array $notes
*/
public function setNotes($notes)
{
$this->notes = $notes;
}
/**
* #return mixed
*/
public function getEmail()
{
return $this->email;
}
/**
* #param mixed $email
*/
public function setEmail($email)
{
$this->email = $email;
}
}
?>
I hope someone can help me
It is because your class's properties are private.
An example class with only private properties ...
php > class Foo { private $bar = 42; }
php > $obj = new Foo();
do not expose values:
php > echo json_encode($obj);
{}
But an example class with public properties ...
php > class Bar { public $foo = 42; }
php > $objBar = new Bar();
do it!
php > echo json_encode($objBar);
{"foo":42}
\JsonSerializable
PHP provide an'interafce \JsonSerializable that require a method jsonSerialize. This method is automatically called by json_encode().
class JsonClass implements JsonSerialize {
private $bar;
public function __construct($bar) {
$this->bar = $bar;
}
public function jsonSerialize() {
return [
'foo' => $this->bar,
];
}
}
I prefer this solution because is not good to expose publicly properties
serialization and unserialization ...
If you need to serialize and unserialize php object you can ...
php > class Classe { public $pub = "bar"; }
php > $obj = new Classe();
php > $serialized = serialize($obj);
php > $original = unserialize($serialized);
php > var_dump($original);
php shell code:1:
class Classe#2 (1) {
public $pub =>
string(3) "bar"
}
$serialized variable contains O:6:"Classe":1:{s:3:"pub";s:3:"bar";}. As you can see is not a json, but is a format that allow you to recreate original object using unserialize function.
You have a couple of options here.
Option 1: Make your class properties public
Like what sensorario mentioned, change the visibility of your properties so that it is accessible from outside the class, which is where you are calling json_encode.
Option 2: Introduce a method/function within the class to return the encoded JSON object
Have a toJson() function inside your User class.
Of course, there are way more options - such as extending User so that User is not "contaminated", etc.
But yup, the general problem is your private properties.
So, I am trying the get the types of the methods, to instantiate the classes, for example:
I have a class called mycontroller and a simple method called page which has a class Type hint, for example:
class MyController
{
public function page(AnotherClass $class)
{
$class->intro;
}
}
I also have another class, litterly called anotherclass (very original, I know)
class AnotherClass
{
public $intro = "Hello";
}
Okay, so that's the basics, now I am trying to get the type of MYControllers method arguments page: anotherclass
You can see the logic of my code below:
Class Route
{
/**
* Method paramaters
*
* #var array
*/
protected $params;
/**
* The class and method
*
* #var array
*/
protected $action;
/**
* Get the paramaters of a callable function
*
* #return void
*/
public function getParams()
{
$this->params = (new ReflectionMethod($this->action[0], $this->action[1]))->getParameters();
}
/**
* Seperate the class and method
*
* #param [type] $action
* #return void
*/
public function getClassAndMethod($action = null)
{
$this->action = explode("#", $action);
}
/**
* A get request
*
* #param string $route
* #return self
*/
public function get($route = null)
{
if(is_null($route)) {
throw new Exception("the [$route] must be defined");
}
return $this;
}
public function uses($action = null)
{
if(is_null($action)){
throw new Exception("the [$action] must be set");
}
if(is_callable($action)){
return call_user_func($action);
}
// Get the action
$this->getClassAndMethod($action);
// Get the params of the method
$this->getParams();
foreach ($this->params as $param) {
print_R($param->getType());
}
// var_dump($action[0]);
}
}
Which is simply being called like so:
echo (new Route)->get('hello')->uses('MyController#page');
So, what the above does, is it splits the uses method paramater via the # sign, the [0] will be the class and the [1] will be the class's method, then I am simply ReflectionMethod to get the parameters of said method, and then I am trying to get the parameter type, which, is what I am stuck on, because it just keeps returning an empty object:
ReflectionNamedType Object { )
So, my question is, why is it returning an empty object, and how can I get the type of the parameter?
You have to echo instead of print_r :
foreach ($this->params as $param) {
echo $param->getType() ; //AnotherClass
}
Because ReflectionType use __toString() to display it.
Or
foreach ($this->params as $param) {
print_r($param->getClass()) ; //AnotherClass
}
I have the following class
namespace PG\Referrer\Single\Post;
class Referrer implements ReferrerInterface
{
/**
* #var $authorReferrer = null
*/
protected $isAuthorReferrer = null;
/**
* #var $dateReferrer = null
*/
protected $isDateReferrer = null;
/**
* #var $searchReferrer = null
*/
protected $isSearchReferrer = null;
/**
* #var $taxReferrer = null
*/
protected $isTaxReferrer = null;
/**
* #param array $values = null;
*/
public function __construct(array $values = null)
{
if ($values)
$this->setBulk($values);
}
/**
* Bulk setter Let you set the variables via array or object
*/
public function setBulk($values)
{
if (!is_array($values) && !$values instanceof \stdClass) {
throw new \InvalidArgumentException(
sprintf(
'%s needs either an array, or an instance of \\stdClass to be passed, instead saw %s',
__METHOD__,
is_object($values) ? get_class($values) : gettype($values)
)
);
}
foreach ($values as $name => $value) {//create setter from $name
global $wp_query;
if (array_key_exists($value, $wp_query->query_vars)) { //Check that user don't set a reserved query vars
throw new \InvalidArgumentException(
sprintf(
'%s is a reserved query_vars and cannot be used. Please use a unique value',
$value
)
);
}
$setter = 'set' . $name;
$condition = isset($_GET[$value]);
if ($setter !== 'setBulk' && method_exists($this, $setter)) {
$this->{$setter}($condition);//set value (bool)
}
}
return $this;
}
/**
* #param bool $authorReferrer
* #return $this
*/
public function setAuthorReferrer($isAuthorReferrer)
{
$this->isAuthorReferrer = $isAuthorReferrer;
return $this;
}
/**
* #param bool $dateReferrer
* #return $this
*/
public function setDateReferrer($isDateReferrer)
{
$this->isDateReferrer = $isDateReferrer;
return $this;
}
/**
* #param bool $searchReferrer
* #return $this
*/
public function isSearchReferrer($isSearchReferrer)
{
$this->isSearchReferrer = $isSearchReferrer;
return $this;
}
/**
* #param bool $taxReferrer
* #return $this
*/
public function setTaxReferrer($isTaxReferrer)
{
$this->isTaxReferrer = $isTaxReferrer;
return $this;
}
}
with its interface
namespace PG\Referrer\Single\Post;
interface ReferrerInterface
{
/**
* #param array $values
* #return $this
*/
public function setBulk($values);
/**
* #param bool $authorReferrer
* #return $this
*/
public function setAuthorReferrer($isAuthorReferrer);
/**
* #param bool $dateReferrer
* #return $this
*/
public function setDateReferrer($isDateReferrer);
/**
* #param bool $searchReferrer
* #return $this
*/
public function isSearchReferrer($isSearchReferrer);
/**
* #param bool $taxReferrer
* #return $this
*/
public function setTaxReferrer($isTaxReferrer);
}
This class sets up 4 conditionals that I need to use in another class. The values that is used in this class is also set from the other class, so basically the user sets values in the other class (lets call it class b) that is then used by class Referrer and returns the 4 conditionals which is then used by class b.
The reason why I'm doing it this way is because there will be two other classes that will need to do the same, but will returns different info
What is the more correct way to achieve this?
EDIT
To clear this up
class Referrer
The properties $isAuthorReferrer, $isDateReferreretc will either have a value of null or a boolean value depending on what is set by the user.
Example:
$q = new Referrer(['authorReferrer' => 'aq']);
In the code above, $isAuthorReferrer is set via the setBulk() method in the class to true when the variable aq is available in the URL or false when not present. The three other properties will return null because they are not set in the example.
The above works as expected, but I need to do this in another class, lets again call it class b. The arguments will be set to class b, and in turn, class b will set this arguments to class Referrer, class Referrer will use this arguments and return the proper values of its properties, and class b will use this results to do something else
Example:
$q = new b(['authorReferrer' => 'aq']);
Where class b could be something like this (it is this part that I'm not sure how to code)
class b implements bInterface
{
protected $w;
protected $other;
public function __construct($args = [])
{
//Do something here
// Do something here so that we can use $other in other classes or functions
}
public function a()
{
$w = new Referrer($args);
}
public function b()
{
// use $w properties here
// return $other for usage in other classes and functions
}
}
The best way is to inject the referrer to your classes in order to do loose coupling between them and the referrer (this pattern use the benefit of your ReferrerInterface):
class b implements bInterface
{
protected $referrer;
public function __construct(ReferrerInterface $referrer, array $values = array())
{
$this->referrer = $referrer;
$this->referrer->setBulk($values);
}
public function getReferrer()
{
return $this->referrer;
}
public function b()
{
// use $this->referrer properties here
}
}
// Instantiation (use your dependency injection if you have one):
$referrer = new Referrer();
$b = new b($referrer, ['authorReferrer' => 'aq']);
I do not understand what is $other so I removed it but explain me if you want me to I add it again.
If you need to use the properties of the referrer in b, you should add some getters in your ReferrerInterface to allow that. I would use setAuthorReferrer($isAuthorReferrer) to set the value and isAuthorReferrer() to get it for instance.
Hello i'm having some trouble with __get __set in php 5.3 it seems that i have an error there.. already googled about it and seems it's a change of behaviour since php 5 but i didn't find a solution that would work :-(
$config['url']['uri'] = $request->uri();
=>
Notice: Indirect modification of overloaded element of Library\Config\Config has no effect in subdomains/prod/index.php on line 33
use \ArrayObject;
use \ArrayAccess;
use \Countable;
use \IteratorAggregate;
use \ArrayIterator;
use \Twelve\SingletonPattern\LazySingleton;
use \Twelve\SingletonPattern\Interfaces\ILazySingleton;
/**
* Singleton With Configuration Info
*/
class Config extends ArrayObject implements ArrayAccess, Countable, IteratorAggregate, ILazySingleton
{
/**
* Instance Var
* #var Config
*/
protected static $_instance = null;
/**
* Stores FileName
* #var Config
*/
protected static $_configFile = '';
/**
* Config Settings Array
* #var Config
*/
protected $_settings = array();
public static function getInstance(){
return LazySingleton::getInstance(__CLASS__);
}
/**
* Set the Config File Path
*/
public static function setFile($filePath)
{
static::$_configFile = $filePath;
}
public function __construct()
{
LazySingleton::validate(__CLASS__);
// Allow accessing properties as either array keys or object properties:
parent::__construct($this->_settings, ArrayObject::ARRAY_AS_PROPS);
$values = include(static::$_configFile);
if(is_array($values))
{
$this->_settings = $values;
}
}
/**
* Prevent Cloning
*/
public function __clone()
{
trigger_error('Clone is not allowed.', E_USER_ERROR);
// No Cloning Allowed
}
/**
* Returns the Number of Elements Inside the Config File
* #var Config
* #return Integer Number of Elements Inside Config
*/
public function count()
{
return sizeof($this->_settings);
}
/**
* Check if a Given Key Exists
* #var Config
* #param mixed $offset Key of Item to Check
* #return Boolean True if Key Exists, False Otherwise
*/
public function offsetExists($offset)
{
return key_exists($offset, $this->_settings);
}
/**
* Retrieve the Value of a Given Key
* #param mixed $offset Key of Item to Fetch
* #return mixed Value of the Matched Element
*/
public function offsetGet($offset)
{
return $this->_settings[$offset];
}
/**
* Assign a new Value to a Key
* #param mixed $offset Key of the Element to Set
* #param mixed $value Value to Assign
*/
public function offsetSet($offset, $value)
{
$this->_settings[$offset] = $value;
}
/**
* Remove an Item from the Config
* #param mixed $offset Key of the Element to Remove
*/
public function offsetUnset($offset)
{
unset($this->_settings[$offset]);
}
/**
* Retrieve an Iterator for Config Values
* #return Iterator Iterator of Config Values
*/
public function getIterator()
{
return new ArrayIterator($this->_settings);
}
/**
* Enables to Set Values Using the Object Notation i.e $config->myValue = 'Something';
*/
public function __set($key, $value)
{
(array) $this->_settings[$key] = $value;
}
/**
* Enables to Get Values using the Object Notation i.e $config->myValue;
*/
public function &__get($key)
{
return $this->_settings[$key];
}
public function __isset($key)
{
return isset($this->_settings[$key]);
}
}
New Edit:
parent::__construct($this->_settings, ArrayObject::ARRAY_AS_PROPS);