I am kind of confused about using name spaces in php,would try to explain using the problem I have with one of my project
I have a class under a namespace as
namespace BigBlueButton;
class BigBlueButton
{
public function createMeeting($createMeetingParams, $xml = '')
{
//some code
return new CreateMeetingResponse($xml);
}
}
I use it as
use \BigBlueButton;
$bbb = new BigBlueButton\BigBlueButton();
//then I try to reference the variable $bbb in a function but I can't
function easymeet_create_meeting($id) {
// $bbb = new BigBlueButton\BigBlueButton(); I don't want to create a new object here but reference the object I created above,something like this
$bbb=BigBlueButton\bbb;
}
But I can't seem to access the above variable i.e. $bbb , I tried global $bbb ,but $bbb doesn't seem to be in the global namespace, I understand I am trying to access a constant $bbb in the above code, but I showed it anyway to tell what I am trying to do
Namespacing does not extend to variables like that:
In the PHP world, namespaces are designed to solve two problems that
authors of libraries and applications encounter when creating
re-usable code elements such as classes or functions:
Name collisions between code you create, and internal PHP classes/functions/constants or third-party
classes/functions/constants.
Ability to alias (or shorten) Extra_Long_Names designed to alleviate the first problem, improving readability of source code.
http://php.net/manual/en/language.namespaces.rationale.php
Your class declaration is fine, but you've mis-used the use keyword as it should refer to specific classes, not a namespace.
use \BigBlueButton\BigBlueButton;
function easymeet_create_meeting($id, BigBlueButton $bbb) {
/* ... */
}
$bbb = new BigBlueButton();
$result = easymeet_create_meeting(1234, $bbb);
You'll notice that I've also moved $bbb into the function parameters because global state is the devil. It's also type-hinted because why not?
Better yet:
class BigBlueButton implements ButtonInterface {}
class BigRedButton implements ButtonInterface {}
And:
function easymeet_create_meeting($id, ButtonInterface $bbb) {}
$result[] = easymeet_create_meeting(1234, new BigBlueButton());
$result[] = easymeet_create_meeting(5678, new BigRedButton());
And now we're looking at the rudiments of Dependency Injection, which is a good habit to get into.
If I understand it right the problem is not with namespace.
As you said you could try using a global variable for example:
use \BigBlueButton;
global $bbb;
$bbb = new BigBlueButton\BigBlueButton();
function easymeet_create_meeting($id) {
global $bbb;
// you can use it
}
or you can send the variable to the function
function easymeet_create_meeting($id, $bbb) {
}
I do not know the entire problem, but you could also use a singleton design pattern in the class so you always get the same instance every time you use it.
Related
I want to know if there is a way to namespace values, in a similar way that we namespace functions in PHP.
You can have:
namespace pizza\land;
function turn_oven_on(){}
And you can access that function with pizza\land\hello()
I wonder if there is a way to do something similar for values.
This is not correct, I am just illustrating what I mean:
namespace pizza\land;
namespaced $ingredients = array('pepperoni', 'garlic');
Then access it with $pizza\land\ingredients.
Other parts in the same runtime can do:
namespace pasta\land;
namespaced $ingredients = array('tomato', 'mozzarella');
Then access it with $pasta\land\ingredients.
Of course that doesn't work, but it serves as an example of what I mean.
I know there is the obvious way, which would be to use the Singleton pattern where the constructor sets the value of a public property for the singleton instance (the one and only one instance of the class).
I dislike this setup, and in that case I prefer to go the killer route and just do global $pseudonamespaced_pizza_land_ingredients.
I wonder, is there anything else I can do to achieve this setup using the latest version of PHP (now 8.1)?
Why?
To have the same effect you have with global but at the same time avoid collision.
Well, let's say I am working with some procedural code and I need a value that can be accessed across multiple functions.
So I want to use that value within the realms of that bunch of functions.
I do not want to wrap all those functions into one Class and then use a property for that class, because in that case I just prefer the Singleton or the global.
Also, if not possible. Why not?
I cannot believe that this hasn't been mentioned before as something to consider for integration into PHP. So, there must be a reason for this not being possible, if it isn't. It would be a cool solution for all those codebases that are mostly procedural and use global way too often... ehem... WordPress...
I think this could be a good answer for this question as the goal is to have variable live inside of the namespace, and that OP has overcomplicated the question for no reason.
Assuming that you have a variable named $number in the global scope of a PHP script. Inside a function, we have another variable with the same name $number, and we assign and change values of it within the function. But, the global variable remains unchanged. This is variable scope. In the same way, classes can be namespaced to give it scope.
Consider having a following code:
<?php
namespace Math;
function add($a, $b) {
return $a + $b;
}
const PI = 3.14;
class Geometry {
static function getCircleArea($radius) {
return PI * $radius ** 2;
}
}
First, we declare the namespace. (So, all the classes, interfaces, constants and function below it will be items of that namespace)
Then, we declared a normal function.
Then, a class constant.
Then, a class.
Now let's access to theseMath namespace's items from another file(functions, constants, and classes).
<?php
// includes the Math.php file
// It's like you had all the code of Math.php written here
include_once 'Math.php';
echo Math\add(2,3); // 5
echo Math\PI; // 3.14
echo Math\Geometry::getCircleArea(10); // 314.15
Thanks to #shingo's comment under the question, this is how I now learned that this can be achieved.
// Namespace
namespace whatever;
// Class with static property
class StaticStuff {
static string $variable = 'Hey';
}
// Access it
echo StaticStuff::$variable; // Hey
// Edit it
StaticStuff::$variable = 'Miau!';
// Access it again
echo StaticStuff::$variable; // Miau!
Specifically:
Define a class
With a static property
Inside a namespace
I need to change a global variable inside a namespace in PHP.
I'm currently refactoring some legacy code and a section of the code relies on the use of a global variable, which used to be configured in the code I'm refactoring.
I'm now trying to use namespaces on the new refactored code and can access the variable just fine by using \, but I couldn't find a way to change its value.
Sadly, changing the \legacyCode so it doesn't use the global variable is out of question.
Is not using namespaces here the only good solution?
Example:
namespace Namespace;
$GlobalVariable = 123;
abstract class NamespaceClass extends \legacyCode
{
}
// no namespace defined
class legacyCode
{
function doSomething() {
global $GlobalVariable;
echo $GlobalVariable;
// GlobalVariable is undefined
}
}
Thanks in advance.
I have been using active record for quite a while, and I wanted a little change of scenery, some developer friends suggested looking into ORM, all of the ORM projects I have looked at require a separate class extending the ORM class.
My question is: is there any way to dynamically create a class using PHP from within a function without eval?
This is what I have:
<?php
class Constructor
{
function new_class($class)
{
$myself = get_called_class();
eval("class {$class} extends {$myself} { }");
}
function say_hi()
{
$class = get_called_class();
echo "Hi, {$class}!";
}
}
$constructor = new Constructor;
$constructor->new_class("Greeter");
$greeter = new Greeter;
$greeter->say_hi(); // Hi, Greeter!
But, my client informs me that eval is blocked on his environment due to him being on shared hosting.
You probably don't want to do that. But as a workaround, you could use the same approach as via eval(), but once you have constructed the string which you would feed to eval you just write it out as a file and include it again.
Something like this:
function my_eval($str)
{
$filename = uniqid().'.tmp';
file_put_contents ($filename, $str);
include $filename;
unlink ($filename);
}
I've written this from memory and not tested it, but I think it should do the trick. Only caveat I'd see right now is that you'd still essentially be doing the same as eval(), and this variant wouldn't allow you to create variables in the same scope as the calling context (although you could use $GLOBALS[] to get around that for global scope variables).
You can redirect calls to some properties/functions by using __get, __call.
Is there a way to do it for classes?
I would like to convert all mentions of some_class_name in the code to, MY_VERSION_some_class_name (not only for one class, it's a pattern).
This would be easy if methods / properties were the target of this renaming policy.
Can you think of a way to do it for classes in PHP?
Edit: I need this for referencing to different variants of classes in different situations. I need one class name to be resolved to different variants of this class, depending on a condition known at runtime, and persistent through the whole session.
Thanks
p.s.
If you are curious why I want to do this, look at Maintaining variants of an application
You can't convert all mentions of some_class_name in the code to another class. However, you can use variables as class names:
$className = "MyClass";
$obj = new $className;
$className::myMethod();
All you have to do is change the variable and you will be using a different class. If you have to do this for a lot of classes, you might want to create some sort of factory for it.
$factory = System::getFactory();
$obj = $factory->getAuthObj();
Basically, the System class would return a different object based on what class needed to be used for that particular run time.
Aiden's untested approach: variable variables and generating a string:
<?php
$dog = "12";
function getDogClass($myNum)
{
global $dog;
// Do something dynamic here;
$dog = "Dog_" . $myNum;
return "dog";
}
class Dog_13rc1
{
function __construct()
{
printf("Woof!");
}
}
class Dog_12
{
function __construct()
{
printf("Grrrr");
}
}
$myObj = new ${getDogClass('13rc1')}();
?>
As long as the call to getDogClass() returns a string of the name of a variable in scope then you are good.
The output is woof.
This means the only refactoring you need is to find/replace occurences of the class name with that call.
But this is grim. and probably slow. Also, a maintenance/bug-tracking nightmare type of hack.
The magic function you want is __autoload:
function __autoload($class_name) {
// include your special class in here
include $class_name . '.php';
}
Read more here: http://php.net/manual/en/language.oop5.autoload.php
Current situation:
I have the current version of my MVC Framework which uses classes as controllers.
I have some "vintage" modules from my old MVC Framework which uses simple, flat includes as controllers.
Much simplified that means:
New Version:
<?PHP
class blaController extends baseController {
private $intVar;
function dosomethingFunction() {
$this->intVar = 123;
$this->view('myView');
}
}
?>
Old Version:
<?PHP
$globalVar = 123;
// view "controllername" is automatically shown
?>
I'm now trying to write a wrapper to be able to use my old controllers in my new MVC without having to rewrite everything. To do so, I have a "wrapper" controller:
class wrapController extends baseController {
function dosomethingFunction() {
require 'old_dosomething.function.php';
$this->view('old_dosomething_view');
}
}
(Once again: This is VERY, VERY simplified - just to get the idea over. Not actual code.)
The problem with that approach is, that the previously global variable $globalVar now only exists inside of the method "dosomethingFunction" and cannot be accessed by the view.
This wouldn't be the case if I could force the require to behave as "in global scope" so that $globalVar would once again be available in global scope.
So: Is there some way to achieve "require_global" or something similar?
(One solution for my problem would be to modify my old controllers to start with a bunch of "global" commands, but I'd prefer a solution where I don't have to change so much of that old code.)
(Note: Please don't tell me that GLOBALS are bad. It totally misses the point of this question. Just accept that it is a requirement to keep some old code working in a newer, cleaner environment.)
You can add local variables defined within dosomethingFunction() to global scope:
class wrapController extends baseController {
function dosomethingFunction() {
require 'old_dosomething.function.php';
//begin added code
$vararr = get_defined_vars();
foreach($vararr as $varName => $varValue)
$GLOBALS[$varName] = $varValue;
//end added code
$this->view('old_dosomething_view');
}
}
Note, that for this to work as expected, you should call require before using any other thing in the function. get_defined_vars() returns only variables from the current scope, so no array_diff hacks are needed.
This is the easiest solution I can think of.
Use the get_defined_vars() function twice and get a diff of each call to determine what variables were introduced by the required file.
Example:
$__defined_vars = get_defined_vars();
require('old_dosomething.function.php');
$__newly_defined_vars = array_diff_assoc($__defined_vars, get_defined_vars());
$GLOBALS = array_merge($GLOBALS, $__newly_defined_vars);
$this->view('old_dosomething_view');
Hmmm, this is an issue I've never before seen. I suppose you could do this
class wrapController extends baseController {
function dosomethingFunction() {
require 'old_dosomething.function.php';
// Force "old" globals into global scope
$GLOBALS['globalVar'] = $globalVar;
$this->view('old_dosomething_view');
}
}
But that's a pretty tedious, manual process as well, depending on how many globals we're talking about. I'll think about this, but I don't know of any "auto-magic" solution off the top of my head.
For anybody interested: My (so far) final version:
class wrapController extends baseController {
function dosomethingFunction() {
// ... do some initialisation stuff ...
$__defined_vars = array_keys(get_defined_vars());
require 'old_dosomething.function.php';
$__newly_defined_vars = array_diff(
array_keys(get_defined_vars()),
$__defined_vars,
array('__defined_vars')
);
foreach ($__newly_defined_vars as $var) {
$GLOBALS[$var] = &$$var;
}
$this->view('old_dosomething_view');
}
}
Ugly, but it works. Thanks for all your great help!
Have you tried Zend_Registry from Zend Framework?
The registry is a container for storing objects and values in the application space. By storing the value in the registry, the same object is always available throughout your application. This mechanism is an alternative to using global storage.
http://framework.zend.com/manual/en/zend.registry.html