What would be the best practice to do this :
class AwesomeClass {
// Code
public function test()
{
foreach($objects->values as $v)
{
New SuperClass($v);
}
return $objects;
}
}
class SuperClass {
public function __construct($arg2)
{
return trim($arg2);
}
}
$rule_the_world = New AwesomeClass($arg1);
$king = $rule_the_world->test();
The previous code is obviously not working, I think I'm missing some major point of PHP OO.
It's very difficult to decipher what you're asking for, and the code you have is not recoverable.
Code Errors
There are several errors in your code that are illogical:
AwesomeClass has no constructor.
This makes passing arg1 to new AwesomeClass meaningless
arg1 is never initialized
In AwesomeClass::test(), objects is never initialized and has no member value.
You will get a warning since it's not traversable
New SuperClass (should be new, per standards) does nothing.
__construct() cannot return a value.
What You May Want
What I think you're going for is something like this:
class AwesomeClass implements IteratorAggregate {
private $arg1;
public function __construct(array $arg1) {
$this->arg1 = $arg1;
}
public function getIterator() {
return new ArrayIterator($this->arg1);
}
}
class SuperClass {
private $arg2;
public function __construct($arg2) {
$this->arg2 = $arg2;
}
public function __toString() {
return "$this->arg2\n";
}
}
$rule_the_world = new AwesomeClass(array('one', 'two', 'three'));
foreach ($rule_the_world as $sc) {
$sc = new SuperClass($sc);
echo $sc;
}
Note that it is redundant to create an ArrayIterator instance when arg1 must already be an array, this is just an example.
Related
For example:
$m = new mysqli('host', 'user', 'pass', 'db');
$q = $m->query('SELECT stuff FROM table');
while ($row = $q->fetch_assoc()) {
// do stuff
}
How does the query method end up having its own distinct "sub-methods" like fetch_assoc()?
How can I replicate this behaviour using OOP?
EDIT ... would this be considered correct and / or good practice?
class MyClass {
function myMethod() {
return new AnotherClass();
}
}
class AnotherClass {
function __construct() {
$this->stuff = 'stuff';
}
}
Then, I can do:
$obj = new MyClass;
$stuff_getter = $obj->myMethod();
echo $stuff_getter->stuff;
It's a little bit opinion based what you are asking, but because i think a lot of people are strugling with this i will provide some general information about this case.
First of all, you can't say if this is a good practice or not. It depends on the intention and the context. See this code:
class MyClass {
function myMethod() {
return new AnotherClass();
}
}
Is it ok? Yes it's ok what you are doing, but note that you have a strong dependency here. If you ever want for some reason to have different implementations
of AnotherClass you need to change the code.
You can prevent this by using dependency injection. Implement an interface in AnotherClass and inject it into MyClass.
When you have another implementation of AnotherClass you can just pass it like the 'old' version.
How to implement this is also depends on what the intention of your code is but i will provide a basic example of your code.
class MyClass {
private $aClass = null;
function __construct($aClass)
{
$this->aClass = $aClass;
}
function myMethod() {
return new $this->aClass();
}
}
interface AnInterface
{
}
class AnotherClass implements AnInterface {
function __construct() {
$this->stuff = 'stuff';
}
}
$obj = new MyClass(new AnotherClass());
$stuff_getter = $obj->myMethod();
echo $stuff_getter->stuff;
Now using this i can just create another implementation of AnotherClass and pass it to MyClass like i always did. Instead of your scenario where i need to add another function. E.g:
class AnotherClass2 implements AnInterface {
function __construct() {
$this->stuff = 'another stuff';
}
}
$obj = new MyClass(new AnotherClass2());
$stuff_getter = $obj->myMethod();
echo $stuff_getter->stuff;
The second thing i notice is you aren't defining variables. I think it's a little opinion based, but i'm strongly against public variables (which in your case is by default).
Create a variable and assign that variable in your constructor. Create getters and setters (if you are lazy you can create magic getters en setters (see here). You would get something like this:
class AnotherClass implements AnInterface {
private $stuff;
function __construct() {
$this->stuff = 'stuff';
}
public function getStuff()
{
return $this->stuff;
}
}
$obj = new MyClass(new AnotherClass());
$stuff_getter = $obj->myMethod();
echo $stuff_getter->getStuff();
I hope this makes some clearness about your construction, although this might not be fully answering your question.
Two notes on this.
The interface is not always necessary in PHP, but it sure is the best practice.
Instead of implementing an interface you could also use an inheritance stucture if you have a lot of duplication and ofcourse you are allowed to use inheritance (is it an is-a relation?).
Your final code could be (as interface example) something like this:
class MyClass {
private $aClass = null;
function __construct($aClass)
{
$this->aClass = $aClass;
}
function myMethod() {
return new $this->aClass();
}
}
interface AnInterface
{
public function getStuff();
}
class AnotherClass implements AnInterface {
private $stuff;
function __construct() {
$this->stuff = 'stuff';
}
public function getStuff()
{
return $this->stuff;
}
}
class AnotherClass2 implements AnInterface {
private $stuff;
function __construct() {
$this->stuff = 'another stuff';
}
public function getStuff()
{
return $this->stuff;
}
}
$obj = new MyClass(new AnotherClass());
$stuff_getter = $obj->myMethod();
echo $stuff_getter->getStuff();
$obj = new MyClass(new AnotherClass2());
$stuff_getter = $obj->myMethod();
echo $stuff_getter->getStuff();
I am currently digging into the basics of php class / constructor.
I understand how a constructor works but not why I should use it.
For example when I have a constructor like this:
function __construct($arg1, $arg2){
$this->name = $arg1;
$this->speed = $arg2;
}
Why should I use __constructor and not a simple callback like:
function foo($arg1,$arg2){
$this->name = $arg1;
$this->speed = $arg2;
}
Thank you
Doing
$obj = new Class($var1, $var2);
And
$obj = new Class($var1, $var2);
$obj->foo($var1, $var2);
Have the same end result
By forcing values to be passed on the constructor, class can define Mandatory values it should have in order to construct a class. As in the later case, one can ignore foo.
Having a method to initialize means, one ends up having different method names, foo, init etc, constructor avoids this
The constructor is always called on object instantiation and is a known pattern.
Your second example isn't (if it's intended to perform a similar initialisation role as the constructor).
<?php
class abc {
function __construct($arg1, $arg2){
echo $arg1.' '.arg2;
}
}
$obj = new abc('manish','jangir');
?>
It will print "manish jangir" automatically when the object is created
The main purpose is to keep your code clean. With placing your initialization in the constructor you can kan be sure the variable to be used in the other function will be in valid state for example :
class Foo{
private $number;
public function setNumber($number) {
$this->number = $number;
}
public function getNumber() {
if ($this->number=== null) {
throw new RuntimeException("The Number is Null !");
}
return number;
}
}
this is the class with constructor
class Foo{
private $number;
public function __construct($number) {
$this->number = $number;
}
public function getNumber() {
if ($this->number=== null) {
throw new RuntimeException("The Number is Null !");
}
return number;
}
}
with constructor you can be sure the number will be initialized. I hope my answer is clear enough but if you have another question about my answer feel free to ask in the comment :)
We have a lot of existing code that, rather than creating an instance of a class, or using a static function on that class, will call the method on a global singleton of that class.
For example (stringclass.php):
class String {
function endsWith($str, $search) {
return substr($str, -strlen($search)) == $search;
}
}
$STRING_OBJECT = new String();
then it will use this in the following way:
include_once("stringclass.php");
if ($STRING_OBJECT->endsWith("Something", "thing")) {
echo "It's there\n";
}
I realise that this is not a very sensible way of calling the function, but I was wondering if we could fix all the places where people have forgotten to include the right classes using an auto-loader, without changing all the code that uses these singletons. It would detect the use of an undeclared global, and include the correct class file based on the name of the global that was being referenced.
You can use the ArrayAccess interface
http://php.net/manual/en/class.arrayaccess.php
class Ztring implements arrayaccess
{
private $container = array ();
public function offsetSet ($offset, $value)
{
$this->container[$offset] = $value;
}
public function offsetGet ($offset)
{
// exception
if ($offset == 'something')
{
return 'works!';
}
return $this->container[$offset];
}
public function offsetExists ($offset)
{
return isset($this->container[$offset]);
}
public function offsetUnset ($offset)
{
unset ($this->container[$offset]);
}
}
$x = new Ztring ();
$x['zzz'] = 'whatever';
echo $x['zzz']."\n";
echo $x['something']."\n";
Please take a look at this code:
class Foo {
public $barInstance;
public function test() {
$this->barInstance = new Bar();
$this->barInstance->fooInstance = $this;
$this->barInstance->doSomethingWithFoo();
}
}
class Bar {
public $fooInstance;
public function doSomethingWithFoo() {
$this->fooInstance->something();
}
}
$foo = new Foo();
$foo->test();
Question: is it possible to let the "$barInstance" know from which class it was created (or called) without having the following string: "$this->barInstance->fooInstance = $this;"
In theory, you might be able to do it with debug_backtrace(), which as objects in the stack trace, but you better not do it, it's not good coding.
I think the best way for you would be to pass the parent object in Bar's ctor:
class Foo {
public $barInstance;
public function test() {
$this->barInstance = new Bar($this);
$this->barInstance->doSomethingWithFoo();
}
}
class Bar {
protected $fooInstance;
public function __construct(Foo $parent) {
$this->fooInstance = $parent;
}
public function doSomethingWithFoo() {
$this->fooInstance->something();
}
}
This limits the argument to being proper type (Foo), remove the type if it's not what you want. Passing it in the ctor would ensure Bar is never in the state when doSomethingWithFoo() would fail.
I have a singleton factory and would like it to return a reference to the object instance so that I can use the singleton factory to destroy the instance and not have instances elsewhere in my code to survive.
Example of what I would like to be able to do:
$cat = CatFactory::getInstance();
$cat->talk(); //echos 'meow'
CatFactory::destructInstance();
$cat->talk(); //Error: Instance no longer exists
This could work:
<?php
class FooFactory
{
private static $foo;
private function __construct()
{
}
public static function getInstance()
{
return self::$foo ? self::$foo : (self::$foo = new FooFactory());
}
public static function destroyInstance()
{
self::$foo = null;
}
public function __call($fn, $args)
{
if (!method_exists(self::$foo, $fn) || $fn[0] == "_")
throw new BadMethodCallException("not callable");
call_user_func_array(array(self::$foo, $fn), $args);
}
# function hidden since it starts with an underscore
private function _listen()
{
}
# private function turned public by __call
private function speak($who, $what)
{
echo "$who said, '$what'\n";
}
}
$foo = FooFactory::getInstance();
$foo->speak("cat", "meow");
$foo->_listen(); # won't work, private function
FooFactory::destroyInstance();
$foo->speak("cow", "moo"); # won't work, instance destroyed
?>
Obviously it is a hack.
Based on the documentation for unset, I do not think that is possible. You cannot actually destroy an object, only a handle to it. If other variables are around that still hold a reference, the object will continue to live on.
You can accomplish what you want by having your Cat object enforce a private $destroyed property. PHP 5 passes objects by reference by default, so you don't have to worry about that part.
A work around would be creating a cat class
class cat
{
public $cat;
public function __construct()
{
$this->cat = CatFactory::getInstance();
}
public function __destruct()
{
CatFactory::destructInstance();
}
}
$cat = new cat();
$cat->cat->talk();
$cat->cat->talk();