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.
Related
I'm trying to test a method using PHPUnit, where it calls another function (standalone function, without class), which resides in different file which does a some pretty good calculation and returns a object.
This is my actual main code:
class CreateRecords
{
public function createEntities($details)
{
if (trim($details['username']) == "") {
$this->result = "Username is empty.";
} else {
$this->result = create_record($Details['username']);
}
return $this->result;
}
}
This create_record function, (standalone function, without class), which is core function, resides in separate file and it does pretty good calculations (calls lots of other methods/functions) and returns object, whether it is successful or not.
I can mock the createEntities method, but I want to mock the create_record function, which does all the computations and returns the result.
I have seen few posts which has a somewhat similar scenario,
phpunit testing method that calls other class methods which need mock
PHPUnit mock method used in another class
But I am unable to understand, how to mock standalone function which is declared in some different file.
You can create new method that will be returning result from outside function.
Then you can mock this new method
class CreateRecords
{
public function createEntities($details)
{
if (trim($details['username']) == "") {
$this->result = "Username is empty.";
} else {
$this->result = $this->createRecord($Details['username']);
}
return $this->result;
}
public function createRecord($username){
return create_record($username);
}
}
Namespaces in PHP5.3+ offer an excellent solution to this, which allows you to override built-in functions in your current namespace.
Longer term, refactoring the global create_record() into a class which can be injected into your CreateRecords class, and then mocked, would be a good way to go. In this instance, the global function would just be a simple wrapper to call the class until the entire application was updated.
To re-create the create_record($username) is not hard though, and useful for tests. The same technique can be used to override the global time() function as well.
Within the test-file, add a new (but local) namespace:
<?php
namespace Test\DatabaseAccess;
use DatabaseAccess\CreateRecord;
use PHPUnit\Framework\TestCase;
namespace DatabaseAccess {
function create_record($username)
{
// pretend to do something
}
}
class CreateRecordTest extends TestCase
{
// test to check CreateRecord class
// which should call the `create_record`, above
}
This is the same technique that the SymfonyBridge system uses to create ClockMock - which dynamically adds time(), sleep(), etc to the namespace that you are unit-testing (in this example, the namespace DatabaseAccess\CreateRecord, not the Test\ prefixed namespace). ClockMock (and DnsMock) does it with an eval() call, but since you know the namespace explicitly, you can write it into the test file itself for clarity.
After reading the above (pretty good) answers and your comments saying that you cannot touch the tested class - CreateRecords,
I can suggest another solution that is not ideal but should get the job done:
Create a new class that inherits\ extends from CreateRecords - CreateRecordsExtended.
The extended class should override only the tested function in question createEntities($details). so create a new one and copy the code from the original function.
Also, create a new function create_record().
Now, inside the new createEntitied, call your version of create_record - $this->create_record(), instead of calling the global function.
Now you can mock it! and even because this class is used only for testing, you can even just retun whatever you want from it, and do not even have to mock it now.
This class can reside either in the regular code folder, or as a neighbor of your testing class- as it is used only for testing.
Pros:
existing code is not modified at all.
you still use same exact
functionality, besides the mocked function, which is what you wanted.
Cons:
you will be testing a different class then the one you wanted to, but
it still close enough.
code inside function createEntities needs to
be copied
Perhaps its not most ideal, but should get the job done. hope i helped.
I'm just starting out learning PHPUnit. I have a seemingly very simple test;
namespace stats\Test;
use stats\Fetch;
class FetchTest extends \PHPUnit_Framework_TestCase
{
public function setUp()
{
$this->fetch = new Fetch;
}
public function testStoresListOfAssets()
{
$this->assertClassHasStaticAttribute('paths', 'Fetch'); //line 17
}
}
My Fetch class is;
namespace stats;
class Fetch
{
public static $paths = array(
'jquery' => 'http://code.jquery.com/jquery.js'
);
}
The error I get when running PHPUnit;
PHPUnit_Framework_Exception: Argument #2 (string#Fetch)of PHPUnit_Framework_Assert::assertClassHasStaticAttribute() must be a class name
It's probably something very silly but I can't understand the problem
The PHPUnit_Framework_Assert use the PHP method class_exists to check if the classname you have indicated is correct (check this link to see the full code):
if (!is_string($className) || !class_exists($className, FALSE)) {
throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'class name');
}
The problem you have here is the method class_exists doesn't take into account this command:
use stats\Fetch;
So that, you have to indicate the full path to make it work. In this link of stackoverflow you can find more information about that problem. You should change your assert to something like this:
$this->assertClassHasStaticAttribute('paths', '\\stats\\Fetch');
You're not supplying the fully qualified class name and in the context of assertClassHasStaticAttribute() or any other method/function outside of the scope of your (test) class the use statement that complements the class name.
If you're using PHP 5.5 or later (which you should ;) use Fetch::class.
In general you should prefer ::class over strings for class names as modern IDEs can help you with refactoring when changing class names which is close to impossible if you use strings.
To sum it up, for your example it would be:
public function testStoresListOfAssets()
{
$this->assertClassHasStaticAttribute('paths', Fetch::class);
}
Code speaks better than words:
namespaces.php:
<?php
namespace foo;
use foo\models;
class factory
{
public static function create($name)
{
/*
* Note 1: FQN works!
* return call_user_func("\\foo\\models\\$name::getInstance");
*
* Note 2: direct instantiation of relative namespaces works!
* return models\test::getInstance();
*/
// Dynamic instantiation of relative namespaces fails: class 'models\test' not found
return call_user_func("models\\$name::getInstance");
}
}
namespace foo\models;
class test
{
public static $instance;
public static function getInstance()
{
if (!self::$instance) {
self::$instance = new self;
}
return self::$instance;
}
public function __construct()
{
var_dump($this);
}
}
namespace_test.php:
<?php
require_once 'namespaces.php';
foo\factory::create('test');
As commented, if I use the full-qualified name inside call_user_func() it works as expected, but if I use relative namespaces it says the class was not found – but direct instantiations works. Am I missing something or its weird by design?
You have to use the fully qualified classname in callbacks.
See Example #3 call_user_func() using namespace name
<?php
namespace Foobar;
class Foo {
static public function test() {
print "Hello world!\n";
}
}
call_user_func(__NAMESPACE__ .'\Foo::test'); // As of PHP 5.3.0
call_user_func(array(__NAMESPACE__ .'\Foo', 'test')); // As of PHP 5.3.0
I believe this is because call_user_func is a function from the global scope, executing the callback from the global scope as well. In any case, see first sentence.
Also see the note aboveExample #2 Dynamically accessing namespaced elements which states
One must use the fully qualified name (class name with namespace prefix).
In current versions of PHP, the way you have it is the way it is -- when using a string to reference a classname, it needs to be fully qualified with it's complete namespace. It's not great, but that's the way it is.
In the forthcoming PHP v5.5, they will include a feature to address this, by providing a new Classname::class syntax, which you can use instead of putting the FQN classname in a string.
For more info on this, please see the relevant PHP RFC page here: https://wiki.php.net/rfc/class_name_scalars
Your code would look something like this:
return call_user_func([models\$name::class,"getInstance"]);
That may not be exact; I don't have a copy of 5.5 to test with to confirm. But either way, the new syntax will make things a lot better for use cases like yours.
If you try using class_exists() inside a method of a class in PHP you have to specify the full name of the class--the current namespace is not respected. For example if my class is:
<?
namespace Foo;
class Bar{
public function doesBooClassExist(){
return class_exists('Boo');
}
}
And Boo is a class (which properly autoloads) and looks like this
namespace Foo;
class Boo{
// stuff in here
}
if I try:
$bar = new Bar();
$success = $bar->doesBooClassExist();
var_dump($success);
you'll get a false... is there an alternative way to do this without having to explicitly specify the full class name ( i.e. class_exits('Foo\Boo') )?
Prior to 5.5, the best way to do this is to always use the fully qualified class name:
public function doesBooClassExist() {
return class_exists('Foo\Boo');
}
It's not difficult, and it makes it absolutely clear what you're referring to. Remember, you should be going for readability. Namespace imports are handy for writing, but make reading confusing (because you need to keep in mind the current namespace and any imports when reading code).
However, in 5.5, there's a new construct coming:
public function doesBooClassExist() {
return class_exists(Boo::class);
}
The class pseudo magic constant can be put onto any identifier and it will return the fully qualified class name that it will resolve to.......
I want create a helper class that containing method like cleanArray, split_char, split_word, etc.
The helper class it self will be used with many class. example :
Class A will user Helper, Class B, Class C, D, E also user Helper Class
what the best way to write and use helper class in PHP ?
what i know is basic knowledge of OOP that in every Class that use Helper class must create a helper object.
$helper = new Helper();
It that right or may be some one can give me best way to do that.
I also will create XXX Class that may use Class A, B, C, etc.
UPDATE : ->FIXED my fault in split_word method :D
Based on Saul, Aram Kocharyan and alex answer, i modified my code, but its dont work, i dont know why.
<?php
class Helper {
static function split_word($text) {
$array = mb_split("\s", preg_replace( "/[^\p{L}|\p{Zs}]/u", " ", $text ));
return $this->clean_array($array);
}
static function split_char($text) {
return preg_split('/(?<!^)(?!$)/u', mb_strtolower(preg_replace( "/[^\p{L}]/u", "", $text )));
}
}
?>
and i use in other Class
<?php
include "Helper.php";
class LanguageDetection {
public function detectLanguage($text) {
$arrayOfChar = Helper::split_char($text);
$words = Helper::split_word($text);
return $arrayOfChar;
}
}
$i = new Detection();
print_r($i->detectLanguage("ab cd UEEef する ح خهعغ فق 12 34 ٢ ٣ .,}{ + _"));
?>
Helper classes are usually a sign of lack of knowledge about the Model's problem domain and considered an AntiPattern (or at least a Code Smell) by many. Move methods where they belong, e.g. on the objects on which properties they operate on, instead of collecting remotely related functions in static classes. Use Inheritance for classes that share the same behavior. Use Composition when objects are behaviorally different but need to share some functionality. Or use Traits.
The static Utils class you will often find in PHP is a code smell. People will throw more or less random functions into a class for organizing them. This is fine when you want to do procedural coding with PHP<5.2. As of 5.3 you would group those into a namespace instead. When you want to do OOP, you want to avoid static methods. You want your objects to have High Cohesion and Low Coupling. Static methods achieve the exact opposite. This will also make your code less testable.
Are Helper Classes Evil?
Killing the Helper class, part two
Functional Decomposition AntiPattern
Is the word "Helper" in a class name a code smell?
Moreover, every Class that use Helper class must create a helper object is a code smell. Your collaborators should not create other collaborators. Move creation of complex object graphs into Factories or Builders instead.
As a rule of thumb, helpers should contain functionality that is common but has no special designation under the overall architecture of the application.
Suffix the classname with Helper
Use static methods whenever possible
In short:
// Helper sample
//
class ConversionHelper {
static function helpThis() {
// code
}
static function helpThat() {
// code
}
}
// Usage sample
//
class User {
function createThings() {
$received = ConversionHelper::helpThis();
}
}
Instead of creating static class , you should just write simple functions , and include that file at the index/bootstrap file (you can even use namespaces with it).
Instead of:
class Helper {
static function split_word($text) { ...
static function split_char($text) { ...
}
It should be:
namespace Helper;
function split_word($text) { ...
function split_char($text) { ...
There is no point wrapping it all up in a class. Just because you put it in a class doesn't make it object oriented .. actually it does the exact oposite.
You could create a class with static methods...
class Str {
public static function split_char($str, $chr) {
...
}
}
You could also namespace a bunch of functions with a namespace, but I think the former is preferred.
Use public static methods in the class as such:
/* Common utility functions mainly for formatting, parsing etc. */
class CrayonUtil {
/* Creates an array of integers based on a given range string of format "int - int"
Eg. range_str('2 - 5'); */
public static function range_str($str) {
preg_match('#(\d+)\s*-\s*(\d+)#', $str, $matches);
if (count($matches) == 3) {
return range($matches[1], $matches[2]);
}
return FALSE;
}
// More here ...
}
Then invoke them like this:
CrayonUtil::range_str('5-6');
If in another file, use the following at the top:
require_once 'the_util_file.php';