This is probably a stupid question but I can't figure out what to google. I'm learning OOP and get the general concept but trying to implement it and I'm getting some questions.
I'm making a web scraper using simple html dom. I want to create a class which connects to a page and contains several different functions I can call on that page depending on what I want to do. I then can call this classes methods from another file.
This is the class I have written:
<?php
include_once '/simple_html_dom.php';
class CountyScraping
{
protected $demoMode = 0; //set 1 for demo
protected $cinemas = NULL;
protected $url = NULL;
protected $html = NULL;
protected $counties = array();
function __construct($url)
{
$this->setUrl($url);
}
public function setUrl($url)
{
$this->url = $url;
}
public function getUrl()
{
return $this->url;
}
public function setHtml()
{
$this->html = file_get_html($this->url);
}
public function getHtml()
{
return $this->html;
}
public function setCounties()
{
foreach($this->html->find('#UserLocation option') as $element)
{
if (($element->value != NULL) && ($element->plaintext))
{
$county = array();
$county['id'] = $element->value;
$county['county_name'] = $element->plaintext;
$this->counties[] = $county;
}
}
}
public function getCounties()
{
return $this->counties;
}
?>
As you can see from the class above I'm looking to get a list of counties from my page and it's all working as should be. The main method I want to call is to get an array of counties. Using the code above to get a list of counties I have to do something like this currently:
$scrape = new CountyScraping("http://example.com");
$scrape->setHtml();
$scrape->setCounties();
$counties = $scrape->getCounties();
This works fine and I could continue with this however I feel like I'm making a lot of calls to getters and setters from outside the class. I think I should be making one call, getCounties and have everything be handled inside the class.
Am I correct in assuming this? Should my getCounties() method call my setHtml and setCounties methods? Or should I be keeping my getters and setters as minimal as possible and have another function instead?
Any advice about any part of my code would be welcome.
PHP magic methods will help you a bit more also.
Here is a link to the php manual for __set()
http://php.net/manual/en/language.oop5.overloading.php#object.set
here is also a nice articale that will assist you with more information on magic methods
http://culttt.com/2014/04/16/php-magic-methods/
Here is a basic example of magic methods
<?php
class PropertyTest
{
/** Location for overloaded data. */
private $data = array();
/** Overloading not used on declared properties. */
public $declared = 1;
/** Overloading only used on this when accessed outside the class. */
private $hidden = 2;
public function __set($name, $value)
{
echo "Setting '$name' to '$value'\n";
$this->data[$name] = $value;
}
public function __get($name)
{
echo "Getting '$name'\n";
if (array_key_exists($name, $this->data)) {
return $this->data[$name];
}
$trace = debug_backtrace();
trigger_error(
'Undefined property via __get(): ' . $name .
' in ' . $trace[0]['file'] .
' on line ' . $trace[0]['line'],
E_USER_NOTICE);
return null;
}
/** As of PHP 5.1.0 */
public function __isset($name)
{
echo "Is '$name' set?\n";
return isset($this->data[$name]);
}
/** As of PHP 5.1.0 */
public function __unset($name)
{
echo "Unsetting '$name'\n";
unset($this->data[$name]);
}
/** Not a magic method, just here for example. */
public function getHidden()
{
return $this->hidden;
}
}
echo "<pre>\n";
$obj = new PropertyTest;
$obj->a = 1;
echo $obj->a . "\n\n";
var_dump(isset($obj->a));
unset($obj->a);
var_dump(isset($obj->a));
echo "\n";
echo $obj->declared . "\n\n";
echo "Let's experiment with the private property named 'hidden':\n";
echo "Privates are visible inside the class, so __get() not used...\n";
echo $obj->getHidden() . "\n";
echo "Privates not visible outside of class, so __get() is used...\n";
echo $obj->hidden . "\n";
?>
Output is here:
Setting 'a' to '1'
Getting 'a'
1
Is 'a' set?
bool(true)
Unsetting 'a'
Is 'a' set?
bool(false)
1
Let's experiment with the private property named 'hidden':
Privates are visible inside the class, so __get() not used...
2
Privates not visible outside of class, so __get() is used...
Getting 'hidden'
Notice: Undefined property via __get(): hidden in <file> on line 70 in <file> on line 29
This example has been sourced from http://php.net/manual/en/language.oop5.overloading.php#object.set
I would suggest you to always use an SOA (service oriented architecture) when you want to develop with OOP. With SOA your service coming from the class CountyScraper should be unique. This is why we use a singleton. A service shouldn't keep a state from an action. This is why you shouldn't set the url or html as properties.
<?php
include_once '/simple_html_dom.php';
class CountyScraper
{
/**
* Returns the *Singleton* instance of this class.
*
* #staticvar Singleton $instance The *Singleton* instances of this class.
*
* #return Singleton The *Singleton* instance.
*/
public static function getInstance()
{
static $instance = null;
if (null === $instance) {
$instance = new static();
}
return $instance;
}
/**
* Protected constructor to prevent creating a new instance of the
* *Singleton* via the `new` operator from outside of this class.
*/
protected function __construct()
{
}
/**
* Private clone method to prevent cloning of the instance of the
* *Singleton* instance.
*
* #return void
*/
private function __clone()
{
}
/**
* Private unserialize method to prevent unserializing of the *Singleton*
* instance.
*
* #return void
*/
private function __wakeup()
{
}
public function findCounties($url)
{
$counties = array();
$html = file_get_html($url);
foreach ($html->find('#UserLocation option') as $element)
{
if (($element->value != NULL) && ($element->plaintext))
{
$county = array();
$county['id'] = $element->value;
$county['county_name'] = $element->plaintext;
$counties[] = $county;
}
}
}
}
Then, you can use it like that:
$countyScraper = CountyScraper::getInstance();
$countyScraper->findCounties('http://example.com');
Related
I am using a library, and it has the following process to attach many operations to one event:
$action = (new EventBuilder($target))->addOperation($Operation1)->addOperation($Operation2)->addOperation($Operation3)->compile();
I am not sure how to dynamically add operations depending on what I need done.
Something like this
$action = (new EventBuilder($target));
while (some event) {
$action = $action->addOperation($OperationX);
}
$action->compile();
I need to be able to dynamically add operations in while loop and when all have been added run it.
Your proposed solution will work. The EventBuilder provides what is known as a Fluent Interface, which means that there are methods that return an instance of the builder itself, allowing you to chain calls to addOperation as many times as you want, then call the compile method to yield a result. However you are free to ignore the return value of addOperation as long as you have a variable containing an instance of the builder that you can eventually call compile on.
Take a walk with me...
// Some boilerplate classes to work with
class Target
{
private ?string $name;
public function __construct(string $name)
{
$this->name = $name;
}
public function getName(): string
{
return $this->name;
}
}
class Operation
{
private ?string $verb;
public function __construct(string $verb)
{
$this->verb = $verb;
}
public function getVerb(): string
{
return $this->verb;
}
}
class Action
{
private ?Target $target;
private array $operations = [];
public function __construct(Target $target, array $operations)
{
$this->target = $target;
$this->operations = $operations;
}
/**
* Do the things
* #return array
*/
public function run(): array
{
$output = [];
foreach ($this->operations as $currOperation)
{
$output[] = $currOperation->getVerb() . ' the ' . $this->target->getName();
}
return $output;
}
}
Here is a basic explanation of what your EventBuilder is doing under the covers:
class EventBuilder
{
private ?Target $target;
private array $operations = [];
public function __construct(Target $target)
{
$this->target = $target;
}
/**
* #param Operation $operation
* #return $this
*/
public function addOperation(Operation $operation): EventBuilder
{
$this->operations[] = $operation;
// Fluent interface - return a reference to the instance
return $this;
}
public function compile(): Action
{
return new Action($this->target, $this->operations);
}
}
Let's try both techniques and prove they will produce the same result:
// Mock some operations
$myOperations = [
new Operation('Repair'),
new Operation('Clean'),
new Operation('Drive')
];
// Create a target
$target = new Target('Car');
/*
* Since the EventBuilder implements a fluent interface (returns an instance of itself from addOperation),
* we can chain the method calls together and just put a call to compile() at the end, which will return
* an Action instance
*/
$fluentAction = (new EventBuilder($target))
->addOperation($myOperations[0])
->addOperation($myOperations[1])
->addOperation($myOperations[2])
->compile();
// Run the action
$fluentResult = $fluentAction->run();
// Traditional approach, create an instance and call the addOperation method as needed
$builder = new EventBuilder($target);
// Pass our mocked operations
while (($currAction = array_shift($myOperations)))
{
/*
* We can ignore the result from addOperation here, just keep calling the method
* on the builder variable
*/
$builder->addOperation($currAction);
}
/*
* After we've added all of our operations, we can call compile on the builder instance to
* generate our Action.
*/
$traditionalAction = $builder->compile();
// Run the action
$traditionalResult = $traditionalAction->run();
// Verify that the results from both techniques are identical
assert($fluentResult == $traditionalResult, 'Results from both techniques should be identical');
// Enjoy the fruits of our labor
echo json_encode($traditionalResult, JSON_PRETTY_PRINT).PHP_EOL;
Output:
[
"Repair the Car",
"Clean the Car",
"Drive the Car"
]
Rob Ruchte thank you for detailed explanation, one thing I did not include was that each operation itself had ->build() call and I needed to move that to each $builder for it to work.
so a class:
class ToBeUsed
{
private $a;
public function setSomething($a)
{
$this->a = $a;
}
public function getSomething()
{
return $this->a;
}
}
its beign created and updated:
$obj = new ToBeUsed();
$obj->setSomething('a');
and passed to another object
class UseIt
{
/**
* #var ToBeUsed
*/
private $obj;
public function __construct(ToBeUsed $obj)
{
$this->obj = $obj;
}
public function work()
{
$this->obj->getSomething();
$this->obj->setSomething(); //// !!!!! THIS IS BAD!
}
}
now a classic DI example, except that the passed object should be "dulled" - only some methods are allowed to use. E.g. getSomething() is allowed to use, but setSomething() is not. What pattern / practice can get away with it? There used to be friend classes is C but its Php...
class ToBeUsed
{
private $a;
public function setSomething($a)
{
$dbg = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS,2);
if(count($dbg) > 1){
return;
}
$this->a = $a;
}
public function getSomething()
{
return $this->a;
}
}
class UseIt
{
/**
* #var ToBeUsed
*/
private $obj;
public function __construct(ToBeUsed $obj)
{
$this->obj = $obj;
}
public function work()
{
echo $this->obj->getSomething().PHP_EOL; // a
$this->obj->setSomething('b'); // this does nothing
echo $this->obj->getSomething().PHP_EOL; // a
}
}
$obj = new ToBeUsed();
$obj->setSomething('a');
$obj2 = new UseIt($obj);
$obj2->work();
Alternatively, you can perform more complex checks on debug_backtrace() output.
I would probably do something with Interfaces, it doesn't prevent a method form being used. But "they" (whoever they is) would be using it outside of the Interface for $obj.
Like this:
class ToBeUsed implements ToBeUsedInterface
{
private $a;
public function getSomething()
{
return $this->a;
}
public function setSomething($a)
{
$this->a = $a;
}
}
interface ToBeUsedInterface{
public function getSomething();
}
class UseIt
{
/**
* #var ToBeUsed
*/
private $obj;
public function __construct(ToBeUsedInterface $obj)
{
$this->obj = $obj;
}
public function work()
{
$this->obj->getSomething();
$this->obj->setSomething(); //This now exists outside of the interface for $obj
}
}
In terms of IDE's this would prevent the methods from auto-completing as well.
The only other thing I can think of, ( besides the other answer ) would be to set the method to protected and then use ReflectionMethod to change the viability, when you want to use it.
Another Option, is Using Reflection
class ToBeUsed
{
private $a;
public function getSomething()
{
return $this->a;
}
protected function setSomething($a)
{
$this->a = $a;
}
}
$ToBeUsed = new ToBeUsed();
$ReflectionMethod = new ReflectionMethod($ToBeUsed, 'setSomething');
$ReflectionMethod->setAccessible(true);
$ReflectionMethod->invoke($ToBeUsed, 'foo');
echo $ToBeUsed->getSomething();
Outputs:
foo
You can see it live here
And Obviously sense it's protected under normal conditions, it could not be used inside UseIt. If I was going to use this for any amount of code, I would extend or wrap the Reflection class. Just to make the call a bit more concise, like this:
class MyReflector
{
public static function invoke($class, $method, ...$args)
{
$ReflectionMethod = new ReflectionMethod($class, $method);
$ReflectionMethod->setAccessible(true);
$ReflectionMethod->invokeArgs($class, $args);
}
}
$ToBeUsed = new ToBeUsed();
MyReflector::invoke($ToBeUsed,'setSomething', 'foo');
Please note I got all fancy with the variadic ...$arg which is for PHP 5.6+ it just lets you do
MyReflector::invoke($ToBeUsed,'setSomething', 'foo', 'bar');
And $args would be ['foo','bar'] in the first example it's just ['foo'] which can be used for invokeArgs for the second argument which takes an array of arguments to pass on to the actual method.
I've recently learned about the advantages of using Dependency Injection (DI) in my PHP application.
However, I'm still unsure how to create my container for the dependencies. Before, I use a container from a framework and I want to understand how is he doing things in back and reproduce it.
For example:
The container from Zend 2. I understand that the container make class dynamic, he does not have to know about them from the beginning, he checks if he already has that class in his registry and if he has not he check if that class exist and what parameters has inside constructor and put it in his own registry so next time could take it from there, practical is doing everything dynamic and it is completing his own registry, so we do not have to take care of nothing once we implement the container as he can give as any class we want even if we just make that class.
Also if I want to getInstance for A which needs B and B needs C I understand that he doing this recursive and he goes and instantiate C then B and finally A.
So I understand the big picture and what is he suppose to do but I am not so sure about how to implement it.
You may be better off using one of the existing Dependency Containers out there, such as PHP-DI or Pimple. However, if you are looking for a simpler solution, then I've implemented a Dependency Container as part of an article that I wrote here: http://software-architecture-php.blogspot.com/
Here is the code for the container
class Container implements \DecoupledApp\Interfaces\Container\ContainerInterface
{
/**
* This function resolves the constructor arguments and creates an object
* #param string $dataType
* #return mixed An object
*/
private function createObject($dataType)
{
if(!class_exists($dataType)) {
throw new \Exception("$dataType class does not exist");
}
$reflectionClass = new \ReflectionClass($dataType);
$constructor = $reflectionClass->getConstructor();
$args = null;
$obj = null;
if($constructor !== null)
{
$block = new \phpDocumentor\Reflection\DocBlock($constructor);
$tags = $block->getTagsByName("param");
if(count($tags) > 0)
{
$args = array();
}
foreach($tags as $tag)
{
//resolve constructor parameters
$args[] = $this->resolve($tag->getType());
}
}
if($args !== null)
{
$obj = $reflectionClass->newInstanceArgs($args);
}
else
{
$obj = $reflectionClass->newInstanceArgs();
}
return $obj;
}
/**
* Resolves the properities that have a type that is registered with the Container.
* #param mixed $obj
*/
private function resolveProperties(&$obj)
{
$reflectionClass = new \ReflectionClass(get_class($obj));
$props = $reflectionClass->getProperties();
foreach($props as $prop)
{
$block = new \phpDocumentor\Reflection\DocBlock($prop);
//This assumes that there is only one "var" tag.
//If there are more than one, then only the first one will be considered.
$tags = $block->getTagsByName("var");
if(isset($tags[0]))
{
$value = $this->resolve($tags[0]->getType());
if($value !== null)
{
if($prop->isPublic()) {
$prop->setValue($obj, $value);
} else {
$setter = "set".ucfirst($prop->name);
if($reflectionClass->hasMethod($setter)) {
$rmeth = $reflectionClass->getMethod($setter);
if($rmeth->isPublic()){
$rmeth->invoke($obj, $value);
}
}
}
}
}
}
}
/**
*
* #param string $dataType
* #return object|NULL If the $dataType is registered, the this function creates the corresponding object and returns it;
* otherwise, this function returns null
*/
public function resolve($dataType)
{
$dataType = trim($dataType, "\\");
$obj = null;
if(isset($this->singletonRegistry[$dataType]))
{
//TODO: check if the class exists
$className = $this->singletonRegistry[$dataType];
$obj = $className::getInstance();
}
else if(isset($this->closureRegistry[$dataType]))
{
$obj = $this->closureRegistry[$dataType]();
}
else if(isset($this->typeRegistry[$dataType]))
{
$obj = $this->createObject($this->typeRegistry[$dataType]);
}
if($obj !== null)
{
//Now we need to resolve the object properties
$this->resolveProperties($obj);
}
return $obj;
}
/**
* #see \DecoupledApp\Interfaces\Container\ContainerInterface::make()
*/
public function make($dataType)
{
$obj = $this->createObject($dataType);
$this->resolveProperties($obj);
return $obj;
}
/**
*
* #param Array $singletonRegistry
* #param Array $typeRegistry
* #param Array $closureRegistry
*/
public function __construct($singletonRegistry, $typeRegistry, $closureRegistry)
{
$this->singletonRegistry = $singletonRegistry;
$this->typeRegistry = $typeRegistry;
$this->closureRegistry = $closureRegistry;
}
/**
* An array that stores the mappings of an interface to a concrete singleton class.
* The key/value pair corresond to the interface name/class name pair.
* The interface and class names are all fully qualified (i.e., include the namespaces).
* #var Array
*/
private $singletonRegistry;
/**
* An array that stores the mappings of an interface to a concrete class.
* The key/value pair corresond to the interface name/class name pair.
* The interface and class names are all fully qualified (i.e., include the namespaces).
* #var Array
*/
private $typeRegistry;
/**
* An array that stores the mappings of an interface to a closure that is used to create and return the concrete object.
* The key/value pair corresond to the interface name/class name pair.
* The interface and class names are all fully qualified (i.e., include the namespaces).
* #var Array
*/
private $closureRegistry;
}
The above code can be found here: https://github.com/abdulla16/decoupled-app (under the /Container folder)
You can register your dependencies as a singleton, as a type (every time a new object will be instantiated), or as a closure (the container will call the function that you register and that function is expected to return the instance).
For example,
$singletonRegistry = array();
$singletonRegistry["DecoupledApp\\Interfaces\\UnitOfWork\\UnitOfWorkInterface"] =
"\\DecoupledApp\\UnitOfWork\\UnitOfWork";
$typeRegistry = array();
$typeRegistry["DecoupledApp\\Interfaces\\DataModel\\Entities\\UserInterface"] =
"\\DecoupledApp\\DataModel\\Entities\\User";
$closureRegistry = array();
$closureRegistry["DecoupledApp\\Interfaces\\DataModel\\Repositories\\UserRepositoryInterface"] =
function() {
global $entityManager;
return $entityManager->getRepository("\\DecoupledApp\\DataModel\\Entities\\User");
};
$container = new \DecoupledApp\Container\Container($singletonRegistry, $typeRegistry, $closureRegistry);
This Container resolves properties of a class as well as the constructor parameters.
I have done a very simple IoC class which works as intended. I've investigated the IoC and DI pattern and especially after reading this answer. Let me know if something is not right or you have any questions .
<?php
class Dependency {
protected $object = null;
protected $blueprint = null;
/**
* #param $instance callable The callable passed to the IoC object.
*/
public function __construct($instance) {
if (!is_object($instance)) {
throw new InvalidArgumentException("Received argument should be object.");
}
$this->blueprint = $instance;
}
/**
* (Magic function)
*
* This function serves as man-in-the-middle for method calls,
* the if statement there serves for lazy loading the objects
* (They get created whenever you call the first method and
* all later calls use the same instance).
*
* This could allow laziest possible object definitions, like
* adding annotation parsing functionality which can extract everything during
* the call to the method. once the object is created it can get the annotations
* for the method, automatically resolve its dependencies and satisfy them,
* if possible or throw an error.
*
* all arguments passed to the method get passed to the method
* of the actual code dependency.
*
* #param $name string The method name to invoke
* #param $args array The array of arguments which will be passed
* to the call of the method
*
* #return mixed the result of the called method.
*/
public function __call($name, $args = array())
{
if (is_null($this->object)) {
$this->object = call_user_func($this->blueprint);
}
return call_user_func_array(array($this->object, $name), $args);
}
}
/*
* If the object implements \ArrayAccess you could
* have easier access to the dependencies.
*
*/
class IoC {
protected $immutable = array(); // Holds aliases for write-protected definitions
protected $container = array(); // Holds all the definitions
/**
* #param $alias string Alias to access the definition
* #param $callback callable The calback which constructs the dependency
* #param $immutable boolean Can the definition be overriden?
*/
public function register ($alias, $callback, $immutable = false) {
if (in_array($alias, $this->immutable)) {
return false;
}
if ($immutable) {
$this->immutable[] = $alias;
}
$this->container[$alias] = new Dependency($callback);
return $this;
}
public function get ($alias) {
if (!array_key_exists($alias, $this->container)) {
return null;
}
return $this->container[$alias];
}
}
class FooBar {
public function say()
{
return 'I say: ';
}
public function hello()
{
return 'Hello';
}
public function world()
{
return ', World!';
}
}
class Baz {
protected $argument;
public function __construct($argument)
{
$this->argument = $argument;
}
public function working()
{
return $this->argument->say() . 'Yep!';
}
}
/**
* Define dependencies
*/
$dic = new IoC;
$dic->register('greeter', function () {
return new FooBar();
});
$dic->register('status', function () use ($dic) {
return new Baz($dic->get('greeter'));
});
/**
* Real Usage
*/
$greeter = $dic->get('greeter');
print $greeter->say() . ' ' . $greeter->hello() . ' ' . $greeter->world() . PHP_EOL . '<br />';
$status = $dic->get('status');
print $status->working();
?>
I think the code is pretty self-explanatory, but let me know if something is not clear
Because I haven't find anything near what I wanted,I tried to implement on my own a container and I want to hear some opinion about how is looking,because I've start to learn php and oop a month ago a feedback is very important for me because I know I have many things to learn,so please feel free to bully my code :))
<!DOCTYPE html>
<!--
To change this license header, choose License Headers in Project Properties.
To change this template file, choose Tools | Templates
and open the template in the editor.
-->
<?php
class ioc
{
private $defs;
static $instance;
private $reflection;
private function __construct()
{
$defs = array();
$reflection = array();
}
private function __clone()
{
;
}
public static function getInstance()
{
if (!self::$instance) {
self::$instance = new ioc();
}
return self::$instance;
}
public function getInstanceOf($class)
{
if (is_array($this->defs) && key_exists($class, $this->defs)) {
if (is_object($this->defs[$class])) {
return $this->defs[$class];
}
} else {
if (class_exists($class)) {
if (is_array($this->reflection) && key_exists($class, $this->reflection)) {
$reflection = $this->reflection[$class];
} else {
$reflection = new ReflectionClass($class);
$this->reflection[$class] = $reflection;
}
$constructor = $reflection->getConstructor();
if ($constructor) {
$params = $constructor->getParameters();
if ($params) {
foreach ($params as $param) {
$obj[] = $this->getInstanceOf($param->getName());
}
$class_instance = $reflection->newInstanceArgs($obj);
$this->register($class, $class_instance);
return $class_instance;
}
}
if (!$constructor || !$params) {
$class_instance = new $class;
$this->register($class, $class_instance);
return $class_instance;
}
}
}
}
public function register($key, $class)
{
$this->defs[$key] = $class;
}
}
?>
How can I figure out in what class a reference to a variable was initiated (and currently exists)?
Example:
<?php
class MyClass {
public $array = array(
"this",
"is",
"an",
"array"
);
}
$class = new MyClass();
$arrayReference = &$class->array;
GetClassForVariable($arrayReference); //Should return "MyClass"
?>
My best bet is some kind of Reflection, but I haven't found any functions that seem suitable for this.
Edit:
A better suited example for what I want is the following:
<?php
class API_Module {
public $module;
public $name;
private $methods = array();
public function __construct($module, $name) {
$this->module = $module;
$this->name = $name;
$this->methods["login"] = new API_Method($this, "login", "Login");
}
public function GetMethod($method) {
return $this->methods[$method];
}
public function GetURL() {
return $this->module; //Should return "session"
}
}
class API_Method {
public $method;
public $name;
private $parentReference;
private $variables = array();
public function __construct(&$parentReference, $method, $name) {
$this->parentReference = $parentReference;
$this->method = $method;
$this->name = $name;
$this->variables["myvar"] = new API_Variable($this, "myvar");
}
public function GetURL() {
return $this->GetParentURL() . "/" . $this->method; //Should return "session/login"
}
public function GetVariable($variableName) {
return $this->variables[$variableName];
}
private function GetParentURL() {
// Need to reference the class parent here
return $this->parentReference->GetURL();
}
}
class API_Variable {
public $name;
private $parentReference;
public function __construct(&$parentReference, $name) {
$this->parentReference = $parentReference;
$this->name = $name;
}
public function GetURL() {
return $this->GetParentURL() . "/" . $this->name; //Should return "session/login/myvar"
}
private function GetParentURL() {
// Need to reference the class parent here
return $this->parentReference->GetURL();
}
}
$sessionModule = new API_Module("session", "Session");
var_dump($sessionModule->GetMethod("login")->GetVariable("myvar")->GetURL()); //Should return "session/login/myvar"
?>
Now, this works fine, but I'd love to be able to do this without using $parentReference in every single subvariable. It might not be possible, but I'd love to know whether it is or not.
For your example:
$class = new MyClass();
$arrayReference = &$class->array;
GetClassForVariable($arrayReference); //Should return "MyClass"
to find out to which variable originally the alias $arrayReference refers to is not possible in PHP. There is no function available resolving the aliases.
Additionally $class->array is just a variable on it's own. So you would also need to find out based on a value in which class it was defined. That is not possible as well, similar to that PHP does not offer anything to resolve a variable alias, it also does not offer anything to learn about the definition of a variable.
So in short PHP does not have a ReflectionVariable class available ;) I wonder if it is even possible.
The get_class() function should work:
http://php.net/manual/en/function.get-class.php
I agree with GRoNGoR that you shouldn't need to get the parent class of a property of an instantiated object. You could instead just get the name of the class before accessing the property. For example:
$class = new MyClass();
$parent_class = get_class($class); // returns "MyClass"
$arrayReference = &$class->array;
Not sure why you'd need the parent class of the property when you have the object instance and can easily get the parent class from there.
Ive some classes that share some attributes, and i would like to do something like:
$dog = (Dog) $cat;
is it possible or is there any generic work around?
Its not a superclass, or a interface or related in any way. They are just 2 different classes i would like php map the attributes from a cat class to a dog and give me the new object. –
i guess i have to specify a little bit more cause seem like a senseless thing to do.
i've classes that inherits from different parent classes cause i've made an inheritance tree based on the saving method, maybe my bad from the beginning, but the problem is that i have a lot of classes that are practically equal but interacts one with mysql and the other one with xml files. so i have:
class MySql_SomeEntity extends SomeMysqlInteract{}
and
Xml_SomeEntity extends SomeXmlInteract{}
its a little bit deeper tree but the problem its that. i cant make them inherits from the same class cause multiple inheritance is not allowed, and i cant separate current interaction with superclases cause would be a big trouble.
Basically the attributes in each one are practical the same.
since i have a lot of this matching classes i would like to do some generic casting or something like it that can converts (pass the values to each attribute) and but im trying to search the simplest way to everyone of this classes.
You can use above function for casting not similar class objects (PHP >= 5.3)
/**
* Class casting
*
* #param string|object $destination
* #param object $sourceObject
* #return object
*/
function cast($destination, $sourceObject)
{
if (is_string($destination)) {
$destination = new $destination();
}
$sourceReflection = new ReflectionObject($sourceObject);
$destinationReflection = new ReflectionObject($destination);
$sourceProperties = $sourceReflection->getProperties();
foreach ($sourceProperties as $sourceProperty) {
$sourceProperty->setAccessible(true);
$name = $sourceProperty->getName();
$value = $sourceProperty->getValue($sourceObject);
if ($destinationReflection->hasProperty($name)) {
$propDest = $destinationReflection->getProperty($name);
$propDest->setAccessible(true);
$propDest->setValue($destination,$value);
} else {
$destination->$name = $value;
}
}
return $destination;
}
EXAMPLE:
class A
{
private $_x;
}
class B
{
public $_x;
}
$a = new A();
$b = new B();
$x = cast('A',$b);
$x = cast('B',$a);
There is no built-in method for type casting of user defined objects in PHP. That said, here are several possible solutions:
1) Use a function like the one below to deserialize the object, alter the string so that the properties you need are included in the new object once it's deserialized.
function cast($obj, $to_class) {
if(class_exists($to_class)) {
$obj_in = serialize($obj);
$obj_out = 'O:' . strlen($to_class) . ':"' . $to_class . '":' . substr($obj_in, $obj_in[2] + 7);
return unserialize($obj_out);
}
else
return false;
}
2) Alternatively, you could copy the object's properties using reflection / manually iterating through them all or using get_object_vars().
This article should enlighten you on the "dark corners of PHP" and implementing typecasting on the user level.
Without using inheritance (as mentioned by author), it seems like you are looking for a solution that can transform one class to another with preassumption of the developer knows and understand the similarity of 2 classes.
There's no existing solution for transforming between objects. What you can try out are:
get_object_vars() : convert object to array
Cast to Object: convert array to object
You do not need casting. Everything is dynamic.
I have a class Discount.
I have several classes that extends this class:
ProductDiscount
StoreDiscount
ShippingDiscount
...
Somewhere in the code I have:
$pd = new ProductDiscount();
$pd->setDiscount(5, ProductDiscount::PRODUCT_DISCOUNT_PERCENT);
$pd->setProductId(1);
$this->discounts[] = $pd;
.....
$sd = new StoreDiscount();
$sd->setDiscount(5, StoreDiscount::STORE_DISCOUNT_PERCENT);
$sd->setStoreId(1);
$this->discounts[] = $sd;
And somewhere I have:
foreach ($this->discounts as $discount){
if ($discount->getDiscountType()==Discount::DISCOUNT_TYPE_PRODUCT){
$productDiscount = $discount; // you do not need casting.
$amount = $productDiscount->getDiscountAmount($this->getItemTotalPrice());
...
}
}// foreach
Where getDiscountAmount is ProductDiscount specific function, and getDiscountType is Discount specific function.
a better aproach:
class Animal
{
private $_name = null;
public function __construct($name = null)
{
$this->_name = $name;
}
/**
* casts object
* #param Animal $to
* #return Animal
*/
public function cast($to)
{
if ($to instanceof Animal) {
$to->_name = $this->_name;
} else {
throw(new Exception('cant cast ' . get_class($this) . ' to ' . get_class($to)));
return $to;
}
public function getName()
{
return $this->_name;
}
}
class Cat extends Animal
{
private $_preferedKindOfFish = null;
public function __construct($name = null, $preferedKindOfFish = null)
{
parent::__construct($name);
$this->_preferedKindOfFish = $preferedKindOfFish;
}
/**
* casts object
* #param Animal $to
* #return Animal
*/
public function cast($to)
{
parent::cast($to);
if ($to instanceof Cat) {
$to->_preferedKindOfFish = $this->_preferedKindOfFish;
}
return $to;
}
public function getPreferedKindOfFish()
{
return $this->_preferedKindOfFish;
}
}
class Dog extends Animal
{
private $_preferedKindOfCat = null;
public function __construct($name = null, $preferedKindOfCat = null)
{
parent::__construct($name);
$this->_preferedKindOfCat = $preferedKindOfCat;
}
/**
* casts object
* #param Animal $to
* #return Animal
*/
public function cast($to)
{
parent::cast($to);
if ($to instanceof Dog) {
$to->_preferedKindOfCat = $this->_preferedKindOfCat;
}
return $to;
}
public function getPreferedKindOfCat()
{
return $this->_preferedKindOfCat;
}
}
$dogs = array(
new Dog('snoopy', 'vegetarian'),
new Dog('coyote', 'any'),
);
foreach ($dogs as $dog) {
$cat = $dog->cast(new Cat());
echo get_class($cat) . ' - ' . $cat->getName() . "\n";
}
It sounds like what you really want to do is implement an interface.
Your interface will specify the methods that the object can handle and when you pass an object that implements the interface to a method that wants an object that supports the interface, you just type the argument with the name of the interface.
You may think about factories
class XyFactory {
public function createXyObject ($other) {
$new = new XyObject($other->someValue);
// Do other things, that let $new look like $other (except the used class)
return $new;
}
}
Otherwise user250120s solution is the only one, which comes close to class casting.
class It {
public $a = '';
public function __construct($a) {
$this->a = $a;
}
public function printIt() {
;
}
}
//contains static function to 'convert' instance of parent It to sub-class instance of Thing
class Thing extends it {
public $b = '';
public function __construct($a, $b) {
$this->a = $a;
$this->b = $b;
}
public function printThing() {
echo $this->a . $this->b;
}
//static function housed by target class since trying to create an instance of Thing
static function thingFromIt(It $it, $b) {
return new Thing($it->a, $b);
}
}
//create an instance of It
$it = new It('1');
//create an instance of Thing
$thing = Thing::thingFromIt($it, '2');
echo 'Class for $it: ' . get_class($it);
echo 'Class for $thing: ' . get_class($thing);
Returns:
Class for $it: It
Class for $thing: Thing
I think that the best approach is to just create a new instance of a class and than assign the object. Here's what I would do:
public function ($someVO) {
$someCastVO = new SomeVO();
$someCastVO = $someVO;
$someCastVO->SomePropertyInVO = "123";
}
Doing this will give you code hinting in most IDEs and help ensure you are using the correct properties.
If the object you are trying to cast from or to has properties that are also user-defined classes, and you don't want to go through reflection, you can use this.
<?php
declare(strict_types=1);
namespace Your\Namespace\Here
{
use Zend\Logger; // or your logging mechanism of choice
final class OopFunctions
{
/**
* #param object $from
* #param object $to
* #param Logger $logger
*
* #return object
*/
static function Cast($from, $to, $logger)
{
$logger->debug($from);
$fromSerialized = serialize($from);
$fromName = get_class($from);
$toName = get_class($to);
$toSerialized = str_replace($fromName, $toName, $fromSerialized);
$toSerialized = preg_replace("/O:\d*:\"([^\"]*)/", "O:" . strlen($toName) . ":\"$1", $toSerialized);
$toSerialized = preg_replace_callback(
"/s:\d*:\"[^\"]*\"/",
function ($matches)
{
$arr = explode(":", $matches[0]);
$arr[1] = mb_strlen($arr[2]) - 2;
return implode(":", $arr);
},
$toSerialized
);
$to = unserialize($toSerialized);
$logger->debug($to);
return $to;
}
}
}
You can opt for this example below. Hope it will help.
/** #var ClassName $object */
$object->whateverMethod() // any method defined in the class can be accessed by $object
I know this is not a cast but it can be useful sometimes.
PHP provides a very simple way of doing this by using:
(object) ['id'=>1,'name'=>'cat']
https://www.php.net/manual/en/language.types.object.php
In your case you try this:
$dog = json_encode($dog);
$cat = (object) json_decode($dog)
More optimize method is:
$dog = (array)$dog;
$dog['newfield'] = 'xyz';
$dog = (object)$dog;