I've never developed before and I'm a bit puzzled, what is wrong with my syntax here?
private static $instance; //holder of Mongo_Wrapper
public $connected = true;
private $mongo = null; // The mongo connection affiliation
private $database = null; // The database we are working on
with this function:
public function mongo_connect($db_name) {
if (! self::connected) {
$this->mongo = new Mongo;
//TODO: error handle this whole sharade: throw new Kohana_Database_Exception('Cant connect', NULL, 503);
$this->connected = true;
}
$this->database = $this->mongo->$db_name; //set the database we are working on
return $connected;
}
I'm sorry, wmd-editor is giving me hell posting the code.
Thank you!
edit: $connected isn't static, the problem is it isn't working either with static or with $this. Also, this is a singleton class, I don't know if this is important or not.
edit: this is the rest of the code, here self and this worked properly:
public static function singleton($db_name) {
if (!isset(self::$instance)) {
$c = __CLASS__;
$this->$instance = new $c;
}
self::mongo_connect($db_name);
return self::$instance;
}
enter code here
if (! self::connected) {
is probably the cause of your error. You only use self when you are trying to access static class members (which connected is not), and you have to use the $-Sign at the beginning, otherwise you are asking for a class constant. So you either have to declare connected as static, or use $this-> to access it.
Take a look at static class members in the PHP manual!
Also you should really try to understand how OOP works, before writing code like this. PHP tells you that you cannot use $this, because you are not in a object context, which means that you never created an object instance using the new.
Maybe the PHP OOP Basics will help you.
Unfortunately, PHP lets you call methods statically which aren't actually, which may be causing the error here. But sooner or later (probably sooner) you will need to understand the OOP basics anyway, so play around with a few simple classes before trying to write code for productive use.
Also take a look at this sample implementation of the singleton pattern.
If you need further help on this issue, please show us how you are calling the connect method!
There we have your problem. Your are doing the following:
self::mongo_connect($db_name);
Which means "call mongo_connect statically on self".
What you actually need to do is:
self::$instance->mongo_connect();
Which is equivalent to "call mongo_connect on the singleton instance of self".
But please take a closer look on a basic PHP tutorial, because what you are doing there in your code is mostly wrong...
$this->$instance = new $c;
Is wrong in so many ways... Not only because you are using $this in a static context, but also because you are assigning the created instance to a class member with the name which is *contained in $instance, which seems to be empty... No clue how this can actually work...
x3ro is right. You also need the $this->connected syntax at the end:
return $this->connected;
If you're getting an error message when you use $this->connected, it's because your function isn't a method on the class, but a global function.
self should be used with static members (use $this->connected instead of self::connected).
UPDATE
private static function mongo_connect($db_name, $instance)
{
if (!$instance->connected) {
....
}
...
return $instance->connected;
}
public static function singleton($db_name) {
if (!isset(self::$instance)) {
$c = __CLASS__;
self::$instance = new $c;
}
self::mongo_connect($db_name, self::$instance );
return self::$instance;
}
Related
I am testing a class, let's call it ClassUnderTest using another class, let's call it OtherClass. In my Test I do:
$OtherClassStub = $this->createStub(OtherClass::class);
$OtherClassStub->method(...)
->willReturn(...);
$ClassUnderTest->otherClass = $OtherClassStub;
That works. But when the $ClassUnderTest calls new OtherClass(), the original OtherClass class is created instead of the stub.
How can I achieve that every possible instance of OtherClass in the context of the test is replaced by the stub?
From your description I infer that in principle you have something like this:
class OtherClass {
protected function someMethod(): bool
{
// determine $x ...
return $x;
}
}
class ClassUnderTest {
public OtherClass $otherClass;
public function methodToBeTested(): bool
{
$otherClass = new OtherClass();
return $otherClass->someMethod();
}
}
class ClassUnderTestTest extends TestCase {
public function testMethodToBeTested(): void
{
$otherClassStub = $this->createStub(OtherClass::class);
$otherClassStub->method('someMethod')
->willReturn(true);
$classUnderTest = new ClassUnderTest();
$classUnderTest->otherClass = $otherClassStub;
$result = $classUnderTest->methodToBeTested();
$this->assertTrue($result);
}
}
Now the assertion in your test may hold or it may fail. Why? Because you are not calling the method you stubbed on the $otherClassStub. Instead you instantiate a new $otherClass object in the method you're testing (or somewhere down the line).
Either your ClassUnderTest should always use the OtherClass object from the ClassUndertTest::otherClass attribute (assuming that's why you put it there in the first place).
Or you could use some other form of dependency injection, e.g. by using a framework like Symfony or Laravel. (In the case of Symfony you can even use only the DependencyInjection Component, no idea if that's possible with Laravel, too.)
The simple answer to your actual question is: you cannot change the behaviour of the new keyword. Calling new on a class will always instantiate a new object based on exactly that class, unless the constructor of that class defines something else.
(You might want to get the concept of classes and objects straight, your code example as well as your question seem to indicate that you're not quite clear on that. Maybe reading up on that as well as on the concept of dependency injection will help you.)
Perhaps a solution to your problem is presented here:
How to Build a PHP Plugin Module System
This is one way to load classes as plugins and they can be called from each other. With modifying this system a bit, you can create as many "new OtherClass()" as you like from your code and still access everything from other classes. If you want multiple instances of a class, perhaps modify it into this direction:
function load ($module,$instance) {
if (isset($this->$module->$instance)) { return true; }
From above link:
<?php
class Core {
// (A) PROPERTIES
public $error = ""; // LAST ERROR MESSAGE
public $pdo = null; // DATABASE CONNECTION
public $stmt = null; // SQL STATEMENT
public $lastID = null; // LAST INSERT/UPDATE ID
// (B) LOAD SPECIFIED MODULE
// $module : module to load
function load ($module) {
// (B1) CHECK IF MODULE IS ALREADY LOADED
if (isset($this->$module)) { return true; }
// (B2) EXTEND MODULE ON CORE OBJECT
$file = PATH_LIB . "LIB-$module.php";
if (file_exists($file)) {
require $file;
$this->$module = new $module();
// EVIL POINTER - ALLOW OBJECTS TO ACCESS EACH OTHER
$this->$module->core =& $this;
$this->$module->error =& $this->error;
$this->$module->pdo =& $this->pdo;
$this->$module->stmt =& $this->stmt;
return true;
} else {
$this->error = "$file not found!";
return false;
}
}
}
ps. thank you for the mod, who made me work a bit more to keep this answer online. the answer is so much better now.
I'm trying to learn about OOP, and create a singleton to return a settings object within a PHP script. For some reason, the instance never seems to get set.
I'm echoing a message getting settings anytime the singleton goes through its construct (it's silent if it just returns the existing instance).
When this is called from within a script, I'm getting dozens of these getting settings messages, one for each time I call mySettings::getSettings()-- $instance doesn't seem to be created, even when I try to dump it immediately after it is created.
Can anyone point out where I'm going wrong?
Here's the code:
class mySettings {
private $settings;
private static $instance;
private function __construct(){
$configfile = "myconfig.ini";
if(!$this->settings = parse_ini_file( $configfile )){
die('CONFIG NOT FOUND/PARSED');
}
}
public static function getSettings(){
if ( !self::$instance ){
echo "getting settings-- no instance found!";
self::$instance = new mySettings();
var_dump(self::$instance); // <-- dumps nothing
}
return self::$instance;
}
public function setParam( $key, $value ){
$this->settings[$key] = $value;
}
public function getParam( $key ){
return $this->settings[$key];
}
}
Your approach to creating a singleton looks correct.
Try with an empty constructor, there might be an issue with that die() statement there.
Does: // <-- dumps nothing mean you see a "NULL" being rendered or nothing at all?
Maybe you've got some output buffering that hides the var_dump() output.
In PHP why can't I do:
class C
{
function foo() {}
}
new C()->foo();
but I must do:
$v = new C();
$v->foo();
In all languages I can do that...
Starting from PHP 5.4 you can do
(new Foo)->bar();
Before that, it's not possible. See
One of many question asking the same on SO
Discussion on PHP Internals
Another Discussion on PHP Internals
Relevant Feature Request in BugTracker
But you have some some alternatives
Incredibly ugly solution I cannot explain:
end($_ = array(new C))->foo();
Pointless Serialize/Unserialize just to be able to chain
unserialize(serialize(new C))->foo();
Equally pointless approach using Reflection
call_user_func(array(new ReflectionClass('Utils'), 'C'))->foo();
Somewhat more sane approach using Functions as a Factory:
// global function
function Factory($klass) { return new $klass; }
Factory('C')->foo()
// Lambda PHP < 5.3
$Factory = create_function('$klass', 'return new $klass;');
$Factory('C')->foo();
// Lambda PHP > 5.3
$Factory = function($klass) { return new $klass };
$Factory('C')->foo();
Most sane approach using Factory Method Pattern Solution:
class C { public static function create() { return new C; } }
C::create()->foo();
From PHP 5.4 you CAN do: (new Foo())->someMethod();
In PHP, you can't call an arbitrary method on a freshly created object like new Foo()->someMethod();
Sorry, but that's the way it is.
But you could build a work around like this:
<?php
class CustomConstructor
{
public static function customConstruct($methodName)
{
$obj = new static; //only available in PHP 5.3 or later
call_user_method($methodName, $obj);
return $obj;
}
}
Extend CustomContructor like this:
class YourClass extends CustomConstructor
{
public function someCoolMethod()
{
//cool stuff
}
}
And instantiate them like this:
$foo = YourClass::customConstruct('someCoolMethod');
I have not tested it but this or something like it should work.
Correction: This will only work in PHP 5.3 and later since late static binding is required.
You should not be able to execute code like
new C()->foo();
in other languages, at least not as long as that language accurately follows logic. The object is not just created using C(), but with the full new C(). Therefore, you should hypothetically be able to execute that code if you include another pair of parentheses:
(new C())->foo();
(Be warned: I haven't tested the above, I'm just saying it should hypothetically work.)
Most languages (that I've encountered) deal with this situation the same way. C, C#, Java, Delphi...
I tried this and was successful -
<?php
$obj = new test("testFunc");
class test{
function __construct($funcName){
if(method_exists($this, $funcName)){
self::$funcName();
}
}
function testFunc(){
echo "blah";
return $this;
}
}
?>
I need to call a static function from an object using the Singleton design, but using a variable as the class name.
The best way, $class::getInstance();, is only available in PHP 5.3, and the other way I found, call_user_func(array($class, 'getInstance'));, results in the maximum execution time being breached. Does anyone know why this is happening, or of a way for this to work / a workaround?
I know that this is not the best way for things to be done, and the Singleton design pattern would not be my first choice, but unfortunately it's not up to me.
Thanks in advance to anyone who contributes :)
I include the rest of the code involved:
abstract class Library
{
protected function __construct(){}
final private function __clone(){}
final public static function &getInstance()
{
static $libs = array();
$lib = get_called_class();
if(!isset($libs[$lib])) $libs[$lib] = new $lib();
return $libs[$lib];
}
}
public function &loadLibrary($lib)
{
// Filter $lib, and load the library class file...
// Following only works in PHP 5.3
// return $lib::getInstance();
// Following results in maximum execution time being breached.
return call_user_func(array($lib, 'getInstance'));
}
}
$someLibrary =& loadLibrary('someLibrary');
someLibrary.php:
class someLibrary extends Library
{
protected function __construct(){}
// Code...
}
Soulmerge make a valid point saying that get_called_class() is only in PHP 5.3, and therefore I must be using it, but alas, I just cheat my way round things as I usually do (Thanks to Chris Webb from http://www.septuro.com/ for the code - far too complex to be any of my own!).
if(!function_exists('get_called_class'))
{
class classTools
{
static $i = 0;
static $fl = null;
static function get_called_class()
{
$bt = debug_backtrace();
if(self::$fl == $bt[2]['file'].$bt[2]['line']) self::$i++;
else {
self::$i = 0;
self::$fl = $bt[2]['file'].$bt[2]['line'];}
$lines = file($bt[2]['file']);
preg_match_all('/([a-zA-Z0-9\_]+)::'.$bt[2]['function'].'/', $lines[$bt[2]['line']-1], $matches);
return $matches[1][self::$i];
}
}
function get_called_class()
{
return classTools::get_called_class();
}
}
I shall go over all my code again, as there must be a loop somewhere. Back to the drawing board I go :(
You should start by determining what it is that takes you into an infinite loop. Does your constructor (someLibrary::__construct()) have any code that directly/indirectly calls Library::getInstance(), for example?
EDIT get_called_class() was introduced in PHP 5.3, so if your code actually works, you're already running 5.3
you could try to solve this with eval().
To get you an idea:
$theVar = "relvantClassName";
$someObject = eval($theVar::getInstance());
$result = $someObject->performAction();
I would like to know whether there's a way to chain methods on a newly created object in PHP?
Something like:
class Foo {
public function xyz() { ... return $this; }
}
$my_foo = new Foo()->xyz();
Anyone know of a way to achieve this?
In PHP 5.4+, the parser's been modified so you can do something like this
(new Foo())->xyz();
Wrap the instantiation in parenthesis, and chain away.
Prior to PHP 5.4, when you're using the
new Classname();
syntax, you can't chain a method call off the instantiation. It's a limitation of PHP 5.3's syntax. Once an object is instantiated, you can chain away.
One method I've seen used to get around this is a static instantiation method of some kind.
class Foo
{
public function xyz()
{
echo "Called","\n";
return $this;
}
static public function instantiate()
{
return new self();
}
}
$a = Foo::instantiate()->xyz();
By wrapping the call to new in a static method, you can instantiate a class with method call, and you're then free to chain off that.
Define a global function like this:
function with($object){ return $object; }
You will then be able to call:
with(new Foo)->xyz();
In PHP 5.4 you can chain off a newly instantiated object:
http://docs.php.net/manual/en/migration54.new-features.php
For older versions of PHP, you can use Alan Storm's solution.
This answer is outdated - therefore want to correct it.
In PHP 5.4.x you can chain a method to a new-call. Let's take this class as example:
<?php class a {
public function __construct() { echo "Constructed\n"; }
public function foo() { echo "Foobar'd!\n"; }
}
Now, we can use this: $b = (new a())->foo();
And the output is:
Constructed
Foobar'd!
Further information may be found on the manual: http://www.php.net/manual/en/migration54.new-features.php
Well, this may be an old question but as with a lot of things in programming - eventually the answer changes.
Regarding PHP 5.3, no, you can't chain directly from the constructor. To expand on the accepted answer however, in order to properly accommodate for inheritance, you can do:
abstract class Foo
{
public static function create()
{
return new static;
}
}
class Bar extends Foo
{
public function chain1()
{
return $this;
}
public function chain2()
{
return $this;
}
}
$bar = Bar::create()->chain1()->chain2();
That will work just fine and will return you a new Bar() instance.
In PHP 5.4, however, you can simply do:
$bar = (new Bar)->chain1()->chain2();
Hopefully this helps someone stumbling across the question like I have!
It would be really helpful if they 'fix this' in a future release. I really appreciate the ability to chain (especially when populating collections):
I added a method to the base class of my framework called create() that can be chained off of. Should work with all descendant classes automatically.
class baseClass
{
...
public final static function create()
{
$class = new \ReflectionClass(get_called_class());
return $class->newInstance(func_get_args());
}
...
public function __call($method, $args)
{
$matches = array();
if (preg_match('/^(?:Add|Set)(?<prop>.+)/', $method, $matches) > 0)
{
// Magic chaining method
if (property_exists($this, $matches['prop']) && count($args) > 0)
{
$this->$matches['prop'] = $args[0];
return $this;
}
}
}
...
}
Class::create()->SetName('Kris')->SetAge(36);
Just for the sake of completeness (and for the fun of it...), since nobody seems to have mentioned the solution with the shortest (and least sophisticated) code.
For frequently used short-lived objects, especially when writing test cases, where you typically do lots of object creation, you may want to optimize for typing convenience (rather than purity), and sorta' combine Alan Storm's Foo::instantiate() factory method and Kenaniah's with() global function technique.
Simply make the factory method a global function with the same name as the class!. ;-o (Either add it as a convenience wrapper around the proper static Foo::instantiate() or just move it out there while nobody is looking.)
class Foo
{
public function xyz()
{
echo "Called","\n";
return $this;
}
}
function Foo()
{
return new Foo();
}
$a = Foo()->xyz();
NOTE:
I WOULDN'T DO THIS on production code. While kinda' sexy, this is an abuse on basic coding principles (like "principle of least surprise" (although this is actually rather intuitive syntax), or "don't repeat yourself", esp. if wrapping a real factory method with some parameters, which itself, BTW, is already an abuse of DRY...), plus PHP may change in he future to break code like this in funny ways.