PHP Closures - Getting class name of closure scope origin - php

Case
I am playing around on a laravel project to see if i can use closures for my implementation of a sorting interface, and i noticed that when i dd() my closure, it also shows the class in which the closure was created as a property.
Minimised Code
// in my Order model class, i have a function that will return a closure
public static function defaultSortFunction(){
$sortColumn = property_exists(self::class,'defaultSortingColumn') ? self::$defaultSortingColumn : 'created_at';
return function($p,$n)use($sortColumn){
return $p->$sortColumn <=> $n->$sortColumn;
};
}
// in one of my controller I use for testing, I added these 2 methods for testing
public function index(){
$sortFunction = Order::defaultSortFunction();
$this->someOtherFunction($sortFunction);
return 'done';
}
private function someOtherFunction($fn){
dd($fn);
// $scopeModel = get_class($fn); => Closure
// example of how I can use this value later
// $scopeModel::take(10)->get()->sort($fn);
}
The result of the dd() inside someOtherFunction():
^ Closure($p, $n) {#1308 ▼
class: "App\Order"
use: {▼
$sortColumn: "created_at"
}
}
Question
From the result of the dd() it shows that the closure has a property that shows that it was defined in the class App\Order. Is there any way to access this value?
I have tried get_class($fn) but as expected it gives "Closure", and if i did $fn->class it gives an error saying Closure object cannot have properties.

You may use Reflection API on your closure which is a much cleaner way than debug_backtrace
// in one of my controller I use for testing, I added these 2 methods for testing
public function index(){
$sortFunction = Order::defaultSortFunction();
$this->someOtherFunction($sortFunction);
return 'done';
}
private function someOtherFunction($fn){
$reflectionClosure = new \ReflectionFunction($fn);
dd($reflectionClosure->getClosureScopeClass()->getName());
}
getClosureScopeClass returns a ReflectionClass instance based on the class you need to find and getName finishes the job.

You can of course inject the class name in to the closure via a parameter in your defaultSortFunction, but that's obviously not so nice.
You should be able extract the calling class yourself from the call stack using:
https://www.php.net/manual/en/function.debug-backtrace.php
If you use the limit parameter you should be able to restrict it to only returning the calling class and no further back.
I don't know for sure, but I suspect it isn't particularly performant.

Related

PHP return array if breaking chain in singleton

I've built a singleton class with chaining methods (to be used in a template).
To make chaining work I need to return new static. It allows the next chain to be added. The problem I have is that I don't want to return the static object if there are no more chains.
Example
<?php
class bread {
public static $array;
public static function blueprints() {
static::$array = array('some', 'values');
return new static;
}
public static function fields() {
return static::$array;
}
}
$blueprints = bread::blueprints();
$fields = bread::blueprints()->fields();
print_r($blueprint) // Returns object - FAIL
print_r($fields ) // Returns array - OK
In the example above I want $blueprints to return an array, because there are no more methods chained on it.
How can that be done?
The simple answer is you cannot do what you want.
Method chaining is not a special thing for Php.
For your example
bread::blueprints()->fields();
This is not different than:
$tmp = bread::blueprints();
$tmp->fields();
So because of the Php does not know the context where the result will be used of it cannot change the return type.
Here is another version of this question:
Check if call is method chaining
However, your class can implement ArrayAccess interface.This will allow you to treat the object like an array without casting and you get total control over how the members are used.
You can try this:
$blueprints = (array)bread::blueprints();

OOP- function inside function or one function return value for the other function (simple example inside)

I have 2 code's examples for 1
1)- In the first example- first fuction returns the result and pass it to the second fuction.
$goods_id = $_POST['goods_id'];
$goods_category = $this->getCategory($goods_id);
$goods_list = $this->getGoodsList($goods_category);
function getCategory($goods_id){
...
return $goods_category;
}
function getGoodsList($goods_category){
...
return $goods_list;
}
2)- In the second example- the first fuction uses the second function to get the value and then returns the result.
$goods_id = $_POST['goods_id'];
$goods_list = $this->getGoodsList($goods_id);
function getGoodsList($goods_id){
...
$goods_category = $this->getCategory($goods_id);
...
return $goods_list;
}
function getCategory($goods_id){
...
return $goods_category;
}
What code example i should use ($goods_category won't be used anywhere else)?
Before answering this you should complete the following prerequisites:
Post here the class(es) name(s), properties, methods signatures
Are ::getGoodsList() and ::getCategory($goods_id) methods of the same class or not?
Please use private/protected/private method visibility keywords even if you consider them both public
The two main questions: 1. whether ::getCategory($goods_id) method will ever be used somewhere outside the class visibility scope. 2. whether you need the item category below in the code once more. Answering 4.1 as "no" would yield 4.2 "no" as well.
If 4.1. is "yes" and 4.2. "yes", then declare ::getCategory($goods_id) as public and use your option #1. I.e. assign the category to $goods_category and use it anywhere below.
If 4.1. is "yes" and 4.2. "no", then declare ::getCategory($goods_id) as public and you can rewrite your code to something like
$goods_list = $class->getGoodsList($class->getCategory($goods_id));
Note that one should avoid >3-4 nested method calls. 2 is probably fine (my taste), but more than 5 in one line will make the line long and unreadable. However, it all depends on the app logic and memory usage and other stuff, so not a generic recommendation.
If 4.1. is "no" and 4.2. is "no", then declare it as protected or private if you don't plan to extend the class or you don't need the method in classes-descendants and use your approach #2:
class Util {
public function getGoodsList($goods_id) {
...
$goods_category = $this->getCategory($goods_id);
...
return $goods_list;
}
protected function getCategory($goods_id) {
...
return $goods_category;
}
}

How do I tell mock to call the original method under certain circumstances?

I have a class that would want to mock later in a test:
class Original {
function calculate($a) { ... }
}
in test:
$mock = $this->getMock('Original',['calculate']);
$mock->expcets($this->any())
->will($this->returnValueMap([
[1,'ABC'],
[2,'BCD']
// somehow tell $mock to call the original calculate() here
);
I want to tell $mock to fall back to the original calculate() under certain conditions, for example if arguments do not match any of provided checks. As I understand from reading PhpUnit code the invoker just returns null if no match is found :-?
$mock = $this->getMockBuilder(Original::class)
->setMethodsExcept(['calculate'])->disableOriginalConstructor()
->getMock();
This will call the original calculate function, no matter what.
The point of the Mock is to not call the original as it has a dependency you can not control for the test. When you want to actually test the calculate() function, then call a test and use the normal code with set parameters to get the response you want.
class Original {
function calculate($a) { ... }
}
$mock = $this->getMock('Original',['calculate']);
$mock->expects($this->any())
->will($this->returnValueMap([
[1,'ABC'],
[2,'BCD']
$Orig = new Original();
$this->assertEquals(4, $Orig->calculates('2 * 2');
If the mocking system you use is PHP's Mockery, you could also make the Mock "partial".
When you do, all the methods that are not shouldReceive()d will call the original underlying method.
From the docs:
Partial doubles are useful when we want to stub out, set expectations for, or spy on some methods of a class, but run the actual code for other methods.
What we call a runtime partial, involves creating a test double and then telling it to make itself partial. Any method calls that the double hasn’t been told to allow or expect, will act as they would on a normal instance of the object.
class Foo {
function foo() { return 123; }
}
$foo = mock(Foo::class)->makePartial();
$foo->foo(); // int(123);
$foo->shouldReceive('foo')->andReturn(456);
$foo->foo(); // int(456)
You could do that by using a callback to determine the returned value. If the argument does not match any of the fixed values, do a real call:
$mock = $this->getMock('Original',['calculate']);
$mock->expects($this->any())
->will($this->returnCallback(function($argument) {
$values = array(
1 => 'ABC',
2 => 'BCD'
);
if (isset($values[$argument]) {
return $values[$argument];
} else {
return (new Original)->calculate($argument);
}
});
As a side note, needing this could be a simptom of tests being too general. Every test should check how your system works in a very specific case.
Regards
You could specify only the methods to mock. In this way methods you don't specify follow the original logic:
$myStubObject = $this
->getMockBuilder(Original::class)
->setMethods(["anyButCalculate"])
->getMock();
$myStubObject
->method('calculate')
->withAnyParameters()
->willReturn("expectedResultFromOriginalLogic");

PHP combine $this variable

How to combine two variables to obtain / create new variable?
public $show_diary = 'my';
private my_diary(){
return 1;
}
public view_diary(){
return ${"this->"}.$this->show_diary.{"_diary()"}; // 1
return $this->.{"$this->show_diary"}._diary() // 2
}
both return nothing.
Your class should be like following:
class Test
{
public $show_diary;
function __construct()
{
$this->show_diary = "my";
}
private function my_diary(){
return 707;
}
public function view_diary(){
echo $this->{$this->show_diary."_diary"}(); // 707
}
}
It almost looks from your question like you are asking about how to turn simple variables into objects and then how to have one object contain another one. I could be way off, but I hope not:
So, first off, what is the differnce between an object and a simple variable? An object is really a collection of (generally) at least one property, which is sort of like a variable within it, and very often functions which do things to the properties of the object. Basically an object is like a complex variable.
In PHP, we need to first declare the strucutre of the object, this is done via a class statement, where we basicaly put the skeleton of what the object will be into place. This is done by the class statement. However, at this point, it hasn't actually been created, it is just like a plan for it when it is created later.
The creation is done via a command like:
$someVariable= new diary();
This executes so create a new variable, and lays it out with the structure, properties and functions defined in the class statement.
From then on, you can access various properties or call functions within it.
class show_diary
{
public $owner;
public function __construct()
{
$this->owner='My';
}
}
class view_diary
{
public $owner;
public $foo;
public function __construct()
{
$this->foo='bar';
$this->owner=new show_diary();
}
}
$diary= new view_diary();
print_r($diary);
The code gives us two classes. One of the classes has an instance of the other class within it.
I have used constructors, which are a special type of function that is executed each time we create a new instance of a class - basically each time we declare a variable of that type, the __construct function is called.
When the $diary= new view_diary(); code is called, it creates an instance of the view_diary class, and in doing so, the first thing it does is assigns it's own foo property to have the value 'bar' in it. Then, it sets it's owner property to be an instance of show_diary which in turn then kicks off the __construct function within the new instance. That in turn assigns the owner property of the child item to have the value 'My'.
If you want to access single properties of the object, you can do so by the following syntax:
echo $diary->foo;
To access a property of an object inside the object, you simply add more arrows:
echo $diary->owner->owner;
Like this?
$diary = $this->show_diary . '_diary';
return $this->$diary();

Access class constant and static method from string

I have a string containing the class name and I wish to get a constant and call a (static) method from that class.
<?php
$myclass = 'b'; // My class I wish to use
$x = new x($myclass); // Create an instance of x
$response = $x->runMethod(); // Call "runMethod" which calls my desired method
// This is my class I use to access the other classes
class x {
private $myclass = NULL;
public function __construct ( $myclass ) {
if(is_string($myclass)) {
// Assuming the input has a valid class name
$this->myclass = $myclass;
}
}
public function runMethod() {
// Get the selected constant here
print $this->myclass::CONSTANT;
// Call the selected method here
return $this->myclass::method('input string');
}
}
// These are my class(es) I want to access
abstract class a {
const CONSTANT = 'this is my constant';
public static function method ( $str ) {
return $str;
}
}
class b extends a {
const CONSTANT = 'this is my new constant';
public static function method ( $str ) {
return 'this is my method, and this is my string: '. $str;
}
}
?>
As I expected (more or less), using $variable::CONSTANT or $variable::method(); doesn't work.
Before asking what I have tried; I've tried so many things I basically forgot.
What's the best approach to do this? Thanks in advance.
To access the constant, use constant():
constant( $this->myClass.'::CONSTANT' );
Be advised: If you are working with namespaces, you need to specifically add your namespace to the string even if you call constant() from the same namespace!
For the call, you'll have to use call_user_func():
call_user_func( array( $this->myclass, 'method' ) );
However: this is all not very efficient, so you might want to take another look at your object hierarchy design. There might be a better way to achieve the desired result, using inheritance etc.
in php 7 you can use this code
echo 'my class name'::$b;
or
#Uncomment this lines if you're the input($className and $constName) is safe.
$reg = '/^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$/';
if(preg_match($reg,$className) !== 1 || preg_match($reg,$constName) !== 1)
throw new \Exception('Oh, is it an attack?');
$value = eval("return $className::$constName;");
You can achieve it by setting a temporary variable. Not the most elegant way but it works.
public function runMethod() {
// Temporary variable
$myclass = $this->myclass;
// Get the selected constant here
print $myclass::CONSTANT;
// Call the selected method here
return $myclass::method('input string');
}
I guess it's to do with the ambiguity of the ::, at least that what the error message is hinting at (PHP Parse error: syntax error, unexpected T_PAAMAYIM_NEKUDOTAYIM)
Use call_user_func to call static method:
call_user_func(array($className, $methodName), $parameter);
Classes defined as abstract may not be instantiated, and any class that contains at least one abstract method must also be abstract. Methods defined as abstract simply declare the method's signature - they cannot define the implementation.
When inheriting from an abstract class, all methods marked abstract in the parent's class declaration must be defined by the child; additionally, these methods must be defined with the same (or a less restricted) visibility. For example, if the abstract method is defined as protected, the function implementation must be defined as either protected or public, but not private. Furthermore the signatures of the methods must match, i.e. the type hints and the number of required arguments must be the same. This also applies to constructors as of PHP 5.4. Before 5.4 constructor signatures could differ.
Refer to http://php.net/manual/en/language.oop5.abstract.php
This might just be tangential to the subject but, while searching for my own issue I found that the accepted answer pointed me in the right direction, so I wanted to share my problem & solution in case someone else might be stuck in a similar fashion.
I was using the PDO class and was building some error options from an ini config file. I needed them in an associative array in the form: PDO::OPTION_KEY => PDO::OPTION_VALUE, but it was of course failing because I was trying to build the array with just PDO::$key => PDO::$value.
The solution (inspired from the accepted answer):
$config['options'] += [constant('PDO::'.$key) => constant('PDO::'.$option)];
where everything works if you concatenate the class name and the Scope Resolution Operator as a string with the variable and get the constant value of the resulting string through the constant function (more here).
Thank you and I hope this helps someone else!

Categories