Get namespace of call context - php

I have a very simple question, but the answer might be rather complicated.
"How can I get the namespace of the block where a function call was made?"
So, when I do:
1. <?php
2. namespace TestTest;
3.
4. $myobj->doMethod();
How can $myobj->doMethod() know that the namespace on line 4. is TestTest?

This is only possible if the method is not called from the global scope. That's because there's no real way to backtrace it (please someone correct me here if that's not the case!). Simplest solution is to put your call in a function. It's likely you'll be using a method anyway if you're doing this sort of thing. If that's the case then you can use a combination of the debug_backtrace function and the reflection class:
file1.php:
<?php
namespace myns;
require 'file2.php';
function example($class) {
echo $class->nstest();
}
$class = new \myotherns\someclass();
echo example($class);
file2.php:
<?php
namespace myotherns;
class someclass {
function nstest() {
$backtrace = debug_backtrace();
$caller = end($backtrace);
$reflection = new \ReflectionFunction($caller['function']);
return $reflection->getNamespaceName();
}
}
Or all in one file:
<?php
namespace myns {
$someclass = new \myotherns\someclass();
echo example($someclass);
function example($class) {
echo $class->nstest();
}
}
namespace myotherns {
class someclass {
function nstest() {
$backtrace = debug_backtrace();
$caller = end($backtrace);
$reflection = new \ReflectionFunction($caller['function']);
return $reflection->getNamespaceName();
}
}
}
Here's an online demo
You'll need to tweak it depending on how you end up calling your method, but it should be simple enough.

Related

Use unknown class in the another class

I want to use an unknown class with a variable in another class:
Example
$classandmethod = "index#show";
namespace Lib\abc;
use Lib\xyz;
class abc{
function controller($classandmethod)
{
$a = explode("#", $classandmethod);
use Lib\Controller\.$a[0];
}
}
But maybe it's not true, please help everyone!
From the manual:
The use keyword must be declared in the outermost scope of a file (the global scope) or inside namespace declarations
So you can't use a class where you're trying to do it. Then, to instantiate a class from a different namespace from a variable, rather than useing it, supply the whole namespaced class:
<?php
namespace G4\Lib
{
class A
{
public $a = "test";
}
}
namespace G4
{
class B
{
public $a;
public function __construct($class)
{
$class = "\\G4\\Lib\\".$class;
$this->a = new $class; // here's the magic
}
}
$b = new B("A");
var_dump($b->a); // test
}
Demo
The main problem is that use cannot have a variable as part of it as it's used when parsing the file and the variable is only available at runtime.
This is an example of how you can do what you seem to be after...
<?php
namespace Controller;
ini_set('display_errors', 'On');
error_reporting(E_ALL);
class Index {
public function show() {
echo "Showing...";
}
}
$classandmethod = "Controller\Index#show";
list($className,$method) = explode("#", $classandmethod);
$a= new $className();
$a->$method();
This displays...
Showing...
You could of course say that all of these classes must be in the Controller namespace and so the code will change to
$classandmethod = "Index#show";
list($className,$method) = explode("#", $classandmethod);
$className = "Controller\\".$className;
$a= new $className();
$a->$method();

Passing a class as function parameter

I'm trying to do something like this:
function doSomething($param, Class) {
Class::someFunction();
}
$someVar = doSomething($param, Class);
Is it possible?
To explain better what I'm trying to do. I have a helper function in Laravel to generate unique slugs, so I have to query different tables depending on where the slug is going to be saved.
Actual code I'm trying to write:
$newcat->slug = $helper->uniqueSlug($appname, Apk);
public function uniqueSlug($str, Apk)
{
$slug = Str::slug($str);
$count = Apk::whereRaw("slug RLIKE '^{$slug}(-[0-9]+)?$'")->count();
return $count ? "{$slug}-{$count}" : $slug;
}
Thanks!
You can use the magic ::class constant:
public function uniqueSlug($str, $model)
{
$slug = Str::slug($str);
$count = $model::whereRaw("slug RLIKE '^{$slug}(-[0-9]+)?$'")->count();
return $count ? "{$slug}-{$count}" : $slug;
}
$newcat->slug = $helper->uniqueSlug($appname, Apk::class);
In PHP, classes (or class names) are handled as strings. Since PHP 5.5, you can use YourClass::class to get a fully qualified class name.
If you want to get it in an earlier version of php, you can (if you have already an object of the calss) either do the following:
<?php
$obj = new YourClass();
// some code
$clazz = get_class($obj);
?>
or, you can implement a static method in your class, like this:
<?php
class YourClass {
// some code
public static function getClassName() {
return get_called_class();
}
?>
If you want to pass a class to a function, you can do it like this:
<?php
function do_somthing($arg1, $clazz) {
$clazz::someStaticMethod($arg1);
}
?>
or
<?php
function do_somthing($arg1, $clazz) {
call_user_func(array($clazz, 'someStaticMethod')), $arg1);
}
?>
If you need to call a non-static method of that class, you need to instanciate it:
<?php
function do_somthing($arg1, $clazz) {
$obj = new $clazz();
$obj->someNonStaticMethod();
}
?>
Note: You can use PHP type hinting with passed class names:
<?php
function do_somthing($arg1, MyInterface $clazz) {
$obj = new $clazz();
$obj->someInterfaceMethod();
}
?>
I think you can.
Send the class name as string parameter then use it like below.
$classtr = "yourparam";// param comes from the function call.
$obj = new $classtr;
$obj->method();
Send the class name as string parameter you need use the namespace. For example:
function defineClass()
{
$class = "App\MyClass"; // mention the namespace too
}
function reciveClass($class)
{
$class:: // what do you need,
}

PHP 5.4: Getting Fully-qualified class name of an instance variable

I know there is a static class field on PHP 5.5, but I have to stick to PHP 5.4. Is it possible to get the fully qualified class name from a variable?
Example:
namespace My\Awesome\Namespace
class Foo {
}
And somewhere else in the code:
public function bar() {
$var = new \My\Awesome\Namespace\Foo();
// maybe there's something like this??
$fullClassName = get_qualified_classname($var);
// outputs 'My\Awesome\Namespace\Foo'
echo $fullClassName
}
You should be using get_class
If you are using namespaces this function will return the name of the class including the namespace, so watch out if your code does any checks for this.
namespace Shop;
<?php
class Foo
{
public function __construct()
{
echo "Foo";
}
}
//Different file
include('inc/Shop.class.php');
$test = new Shop\Foo();
echo get_class($test);//returns Shop\Foo
This is a direct copy paste example from here
I know this is an old question, but for people still finding this, I would suggest this approach:
namespace Foo;
class Bar
{
public static function fqcn()
{
return __CLASS__;
}
}
// Usage:
use Foo\Bar;
// ...
Bar::fqcn(); // Return a string of Foo\Bar
If using PHP 5.5, you can simply do this:
namespace Foo;
class Bar
{}
// Usage:
use Foo\Bar;
// ...
Bar::class; // Return a string of Foo\Bar
Hope this helps...
More info on ::class here.
For developers using PHP 8.0 and above, you can now easily do this without get_class.
PHP8 introduced the support for ::class on objects. Example:
public function bar() {
$var = new \My\Awesome\Namespace\Foo();
echo $var::class;
}
Here's the RFC for more info.

can I create a class dynamically with $? (dollar-sign)

In PHP sometimes it would be nice if I could define a function or a class with a variable name like
$myfunctionname="test";
function $myfunctionname(){
//...
}
so it would create the function test()
or with classes too like:
$foo = bar;
class $foo {
// lots of complicated stuff
// ...
}
but this doesen't work. like this it would give parse errors!
Is there a solution to this?
(I know, this is not good practise, but just as a workaround, it would be handy)
EDIT: My actual problem:
I have a framework with a migration process where every migration step is in a separate php include file in a folder.
Each file contains only one migration class that contains the name of the include file.
Because the class has to have that certain name, I would like to create the name of the class to a generic name that is created by the filename constant __FILE__
Yes, you can, but I dont want you to.
$classname = "test";
eval("class {$classname}{ public function hello(){ echo \"hello!\"; } }");
$evil_class = new $classname();
$evil_class->hello(); // echo's "hello!"
now, if you don't mind me I'm going for a shower.
You can use a factory pattern:
class poly_Factory {
public function __construct() {
$class = 'poly';
return new $class();
}
}
If that is anything you want to get to.
http://net.tutsplus.com/tutorials/php/understanding-and-applying-polymorphism-in-php/
Scroll down to step 4, last part...
I know you did not ask for that, but what can your question be good for else?
No. This code throws a parse error on line 3 because of the $:
$foo = 'bar';
class $foo {
function hello() {
echo "World";
}
}
$mybar = new bar();
$mybar->hello();
Result:
Parse error: syntax error, unexpected T_VARIABLE, expecting T_STRING on line 3
And as Jan Dvorak pointed out in the comments: even if you figure out a way to do this, don't do this.
If you want to create a value object you can just use the stdClass builtin type.
$object = new stdClass;
$object->someValue = "Hello World";
echo $object->someValue;
See it in Action
If you want to assign methods then you have to use the magic __call function, here is how I would do it.
class AnonObject{
private $properties = array();
private $methods = array();
public function __get($property){
return array_key_exists($property, $this->properties)?$this->properties[$property]:null;
}
public function __set($property, $value){
if (!is_string($value) && is_callable($value)){
if ($value instanceof \Closure){
// bind the closure to this object's instance and static context
$this->methods[$property] = $value->bindTo($this,get_class($this));
} else {
// invokable objects
$this->methods[$property] = $value;
}
} else {
$this->properties[$property] = $value;
}
}
public function __call($method, $args){
if (array_key_exists($method, $this->methods)){
call_user_func_array($this->methods[$method], $args);
} else {
throw new RuntimeException("Method ".$method." does not exist on object");
}
}
}
See it In Action
Note, as stated by several other people this is bad practice. If the goal of this exercise is to compose the behavior of an instance of an object at runtime a more maintainable solution would be to use the Strategy Pattern

Get value of dynamically chosen class constant in PHP

I would like to be able to do something like this:
class ThingIDs
{
const Something = 1;
const AnotherThing = 2;
}
$thing = 'Something';
$id = ThingIDs::$thing;
This doesn't work. Is there a straightforward way of doing something equivalent? Note that I'm stuck with the class; it's in a library I can't rewrite. I'm writing code that takes arguments on the command line, and I would really like it to take symbolic names instead of id numbers.
Use the constant() function:
$id = constant("ThingIDs::$thing");
Use Reflection
$r = new ReflectionClass('ThingIDs');
$id = $r->getConstant($thing);
If you are using namespaces, you should include the namespace with the class.
echo constant('My\Application\ThingClass::ThingConstant');
Helper function
You can use a function like this:
function class_constant($class, $constant)
{
if ( ! is_string($class)) {
$class = get_class($class);
}
return constant($class . '::' . $constant);
}
It takes two arguments:
Class name or object instance
Class constant name
If an object instance is passed, its class name is inferred. If you use PHP 7, you can use ::class to pass appropriate class name without having to think about namespaces.
Examples
class MyClass
{
const MY_CONSTANT = 'value';
}
class_constant('MyClass', 'MY_CONSTANT'); # 'value'
class_constant(MyClass::class, 'MY_CONSTANT'); # 'value' (PHP 7 only)
$myInstance = new MyClass;
class_constant($myInstance, 'MY_CONSTANT'); # 'value'
<?php
class Dude {
const TEST = 'howdy';
}
function symbol_to_value($symbol, $class){
$refl = new ReflectionClass($class);
$enum = $refl->getConstants();
return isset($enum[$symbol])?$enum[$symbol]:false;
}
// print 'howdy'
echo symbol_to_value('TEST', 'Dude');
If you have a reference to the class itself then you can do the following:
if (defined(get_class($course). '::COURSES_PER_INSTANCE')) {
// class constant is defined
}
My problem was similiar to this subject. When you have the object, but not the class name, you could use:
$class_name = get_class($class_object);
$class_const = 'My_Constant';
$constant_value = constant($class_name.'::'.$class_const);
I know I'm a bit late, but I hope this can help anyway.
Based on Phil's answer, I created a default enumerator class that can be extended.
class DefaultEnum
{
static public function getConstantText(string $constant)
{
try {
// Get child class name that called this method
$child_class = get_called_class();
$reflection = new ReflectionClass($child_class);
$const = $reflection->getConstant($constant);
return $const;
} catch (\ReflectionException $e) {
// ...
}
}
}
class CustomEnum extends DefaultEnum
{
const something = 'abcd';
const something2 = 'ABCD';
}
You can call this method like this
CustomEnum::getConstantText('something');
It will return 'abcd'.
The function get_called_class() is a function that returns the class name that called this method and it works specifically for static methods.
In this case $child_class value will be CustomEnum::class. ReflectionClass accepts strings and object as parameter.

Categories