Closure::fromCallable for imported function - php

I'm playing with PHP and some functional style programming.
I'm using the Functional-PHP library but question is generic to PHP (I'm using 7.2).
I try to create a callable from an imported function but what I get is
TypeError: Failed to create closure from callable: function 'pick' not found or invalid function name
Sample code:
use function Functional\pick;
class A
{
public function execute()
{
$pick1 = \Closure::fromCallable('pick');
}
}

PHP use statements define an alias for the rest of the file, but they won't affect a string referencing an imported function or class.
When you say
use function Functional\pick;
it means that in that file, you can call the Functional\pick function just using pick(...). But if you're using a string to reference it then PHP doesn't know to expand the alias.
The quickest way to resolve this is just to use the fully qualified function name when calling fromCallable:
$pick1 = \Closure::fromCallable('Functional\pick');
echo get_class($pick1);
Closure
Alternatively, if you really wanted to use the alias, you could wrap the call a level deeper with another anonymous function:
use function Functional\pick;
$pick1 = \Closure::fromCallable(function (...$args) { return pick(...$args); });
But that's a lot messier, in my opinion at least.
Edit: There's some decent discussion around this in this recent thread in php-externals

Related

How do I get the function name outside a function in PHP?

Is this possible?
Pseudo-code:
class MyClass
{
function myFunc1()
{
...
}
function myFunc2()
{
echo GET_FUNCTION_NAME($this->myFunc1)
}
}
Wanted output:
myFunc1
In the code above the GET_FUNCTION_NAME method/function/construct/whatever would give back the textual representation of the function name given as parameter.
So the main point would be to get the name of a function as a string from outside the function.
All the code I have found deals with giving a function name via a string (eg. specifying callback methods), but none of them mentions how to get that function name without manually writing it in a string (thus duplicating code in a string and making refactoring harder than needed).
OTOH from inside the function it is easy with eg. __FUNCTION__ variable, so I'm not looking for that.
EDIT
A typical use case would be any callback method.
One example where I confronted this problem is the set_error_handler() method where it awaits a callable as first parameter. The callable can be simplified as a string. The problem is that if I specify the function name as a string, any time in the future when I will do refactoring I will have to take extra care to search for the strings as well and do special handling of them otherwise wrong references will be left there.
Not to mention the principle that any name should be defined once and any other use should refer to that one.
If the problem is that You need to specify a callback not using a string, but the function symbol itself, You can do with a help of anonymous function:
class MyClass {
function call(callable $c) {
...
}
function mycallback() {
...
}
function dosomejob() {
$this->call(function() { $this->mycallback(); })
}
}
From point of view of Your refactoring tool, there's still call to function mycallback, it's not reffered as a string.

import and alias a static method from a class

I am trying to alias a static method from a utility/helper class, the documentation does not give anything regarding static methods and using those defined there doesn't work for static methods (as it seems so).
So say I have this class:
namespace App\Helpers;
class HTTP {
public static function extract_path_from_url( string $url ) {
$parsed_url = wp_parse_url( $url );
if ( ! isset( $parsed_url['path'] ) ) {
return false;
}
return (string) $parsed_url['path'];
}
}
then trying to use it on a different file:
<?php
echo \App\Helpers\HTTP::extract_path_from_url( 'http://example.com/test' );
that one above works
but trying to alias it:
<?php
use \App\Helpers\HTTP\extract_path_from_url as extract_path;
echo extract_path( 'http://example.com/test' );
would output
Fatal error: Uncaught Error: Call to undefined function App\Helpers\HTTP\extract_path_from_url()
even:
<?php
use \App\Helpers\HTTP::extract_path_from_url as extract_path;
echo extract_path( 'http://example.com/test' );
shows this weird error:
Parse error: syntax error, unexpected '::' (T_PAAMAYIM_NEKUDOTAYIM)
Is this possible?
Regards,
Aliasing doesn't magically convert methods into functions, try this instead
<?php
use \App\Helpers\HTTP as extract_path;
echo extract_path::extract_path_from_url( 'http://example.com/test' );
Also (it should go without saying) when you alias this only affects the namespace and class name, not methods of the class. These are generally used for 1 of 2 things. Resolving naming conflicts
use NamespaceOne\Someclass;
use NamespaceTwo\Someclass as SecondClass;
If these were both put without an alias then using
Someclass::method()
Would be ambiguous.
The second thing they can be used for is if you need a lot of classes imported from one namespace. Such as this:
use App\Exceptions\NoFile;
use App\Exceptions\FileSize;
use App\Exceptions\FileType;
throw new NoFile();
throw new FileSize();
throw new FileType();
Can be done this way:
use App\Exceptions as E;
throw new E\NoFile();
throw new E\FileSize();
throw new E\FileType();
Which is not only shorter, but easier to maintain if you change the namespace you have to only change it for the alias and then all is good. So in short it's not really intended for what you want to use it for.
Wrap it
You can always make a wrapper for it:
if(!function_exists('extract_path_from_url')){
function extract_path_from_url($url){
return \App\Helpers\HTTP::extract_path_from_url($url);
}
}
And then call it to your hearts content. Performance wise you do have an extra call by wrapping it, but generally wrappers make it easier to maintain. For example if you rename that method or class, you can change it in the wrapper and everything is good. So there is an argument to be made for either option.
You don't have to check if the function exists, but depending on how your overall system works it may not be a bad idea, so I included it in the example just for the sake of completeness. Personally in a case like this, I don't see any issue putting it right in the same file with the class, just remember to load it. If you are using autoloading the functions won't be included unless you manually load the file or otherwise force it to autoload. Assuming nothing else uses the class first, of course.
One method I have used in the past that I really like, is to make a file named http_functions (classname + _functions) and then add a static method to the class that registers the functions:
class HTTP {
public static function regester_fuctions(){
require 'http_functions.php'
}
}
Then when you call HTTP::regester_fuctions() it autoloads HTTP class and includes all the functional wrappers. In fact I do this very thing in my really super awesome debug print class (queue shameless plug) https://github.com/ArtisticPhoenix/Debug
Just some thoughts, enjoy!
A workaround is to use a namespaced helpers.php file and define 'simple' functions in it, which simply pass through arguments.
// lib/My/Deeply/Nested/Namespace/MyClass.php
<?php
namespace My\Deeply\Nested\Namespace;
class MyClass
{
public static function aVeryUsefulFunction(string $var): string
{
// ...Do some stuff
return $magic;
}
}
// lib/helpers.php
namespace App;
use My\Deeply\Nested\Namespace\MyClass;
function doMagic(...$args)
{
return MyClass::aVeryUsefulFunction(...$args);
}
// templates/my-view.php
<?php use function App\doMagic; ?>
<div>I am doing <?= doMagic('magic') ?>!</div>
Note that by using the spread operator ...$args in my 'pass through' function I can change the requirements of the 'target' function without having to update it in two places.
This will break IDE completion, as it will only know to suggest ...$args rather than string $var. I don't know of a way to docblock a function to tell it to read parameters from another function.
As manual says you can import via use classes, functions and constants. Method of a class (even a static one) is not a function.
So, for example you have:
namespace My\Super\NameSpace;
const MY_CONST = 42;
class MyClass {
public function do() { /* code */ } // this is NOT a function
public static function doStatic() { /* code */ } // this is NOT a function too
}
function my_function() { /* code */ } // this is function
In some other file you can write:
namespace YaNamespace;
use const My\Super\NameSpace\MY_CONST;
use My\Super\NameSpace\MyClass;
use function My\Super\NameSpace\my_function as func_alias;
And that's all items you can import with use.

php pass class to function

I have a class with property transof
class translator {
public function transof($phrase) { gives translation of phrase }
}
Now I want to pass an instance of translator to a function:
function parse($part,$class) {
$class->transof($part);
}
$tr = new translator("project","en");
parse("exception",$tr);
Do anyone know how to do this?
I know this example is to simple, and can be easily written without the use of a function, but in my real world example I would like to be able to use a function.
Of course I can use global $tr in the function, and use it inside the function, but I don't like using global.
Thanks in advance
If you do not want to create object in global code to have better maintainability then you need to modify the signature of the function as follows:
function parse($part,$project,$lang) {
$class = new Translator($project,$lang);
$class->transof($part);
}
parse("exception","project","en");
It does what you intend, the object is passed to the function parse. All you need to do is to include the class definition file in or before the php file which contains parse definition or get use of __autoload() function which will include class definition when needed.
You can also define parse this way:
function parse($part, translator $class) {
$class->transof($part);
}
Then code editors as Aptana etc. will know what class this object is an instance of and will be able to provide you with hints concerning your class structure.

Any way to declare a regular function within a class as a method?

I have a scenario where I'm trying to incorporate several people's PHP work, some of it OOP and some not. I want to pull a library file of functions into a class and have those functions be available to other files that reference the class. I know I can just call the library functions directly, but then I would have to update all of the dependent files to do likewise. Example:
class do_something {
function test_state() {
...
}
if ($this->test_state($var)) {
...
}
}
Where test_state() is identical to the same-named function in the library file, making for redundant code to keep sync'd. That can be changed to:
class do_something {
if (test_state($var)) {
...
}
}
But that creates the aforementioned problem of $this->test_state() not being available to files dependent on the class. What I'd like to be able to do is something like:
class do_something {
public function test_state() = test_state();
if ($this->test_state($var)) {
...
}
}
Obviously, that's a very rough and incorrect example of what I'm trying to do... Is there any way in OOP to make that sort of reassignment, making the method of the same name as the function available within the class?
You can use a workaround to simulate this. In fact you would often want this approach to bolt on closures to objects in PHP. It leverages the magic __call method in PHP to redirect method calls to ordinary functions (beware: no $this available).
class do_something {
function __call($func, $args) {
if (isset($this->$func) && is_callable($this->$func)) {
return call_user_func_array($this->$func, $args);
}
}
}
Then you can "register" functions that you want to allow (or closures) with a simple assignment:
$do_something->function_name = "global_function_name";
$do_something->or_even = array("other_class", "method");
But again, this doesn't make them proper methods as such.
You'd create your base utility class, then extend it. See PHP's manual entry for inheritance for the details. I'm not saying this is the best solution for your exact situation, but I think it answers the question you were trying to get at.
What you're asking for isn't possible directly, but can be faked with a quick (horrible) hack:
class do_something {
public function test_state($param) {
return test_state($param);
}
...
$this->test_state($param);
...
}
Good luck with refactoring!

PHP create_function Instance variable - Unable to call anonymous function: Follow up

This is somewhat a follow up to a previous question - but I've distilled the question down and have the "works" vs. "doesn't work" cases narrowed down much more precisely.
My Goal:
I have a class MyClass that has an instance variable myFunction. Upon creating a MyClass object (instantiating), the constructor assigns the instance variable myFunction with the result of a call to create_function (where the code and args come from a db call).
Once this object of type MyClass is created (and stored as an instance variable of another class elsewhere) I want to be able to call myFunction (the instance variable anonymous function) from "anywhere" that I have the MyClass object.
Experimental Cases -- below is my highly simplified test code to illustrate what works vs. what doesn't (i.e. when the expected functionality breaks)
class MyClass extends AnotherClass {
public $myFunction;
function __construct() {
$functionCode = 'echo "NyanNyanNyan";';
$this->myFunction();
/*Now the following code works as expected if put in here for testing*/
$anonFunc = $this->myFunction;
$anonFunc(); //This call works just fine (echos to page)!
/*And if i make this call, it works too! */
self::TestCallAnon();
}
public function TestCallAnon() {
$anonFunc2 = $this->myFunction;
$anonFunc2();
}
}
However, if I do the following (in another file, it errors saying undefined function () in... within the Apache error log.
//I'm using Yii framework, and this is getting the user
//objects instance variable 'myClass'.
$object = Yii::app()->user->myClass;
$object->TestCallAnon(); // **FAILS**
or
$func = $object->myFunction;
$func(); // ** ALSO FAILS **
In addition, several variations of calls to call_user_func and call_user_func_array don't work.
If anyone is able to offer any insight or help that would be great :).
Thanks in advance!
You can't pass references to functions around in PHP like you can in for instance JavaScript.
call_user_func has limited functionality. You can use it like so:
class MyClass {
function func() {}
static function func() {}
}
function myfunc() {}
$i = new MyClass();
call_user_func("myfunc", $args);
call_user_func(array($i, "func"), $args);
call_user_func(array(MyClass, "staticFunc"), $args);
I ended up solving this issue via a workaround that ended up being a better choice anyways.
In the end I ended up having a static class that had a method to randomly return one of the possible identifiers, and then another method which accepted that identifier to build the anonymous function upon each class.
Slightly less elegant than I would like but it ends up working well.
Thanks to everyone for your efforts.

Categories