Im trying to pass objects between classes in code igniter and am currently failing. What am I doing wrong. Let me strt showing the pure.php version
Errors.php
<?php
class Errors
{
public function __construct(){}
public function setError($msg){}
}
OtherClass.php
<?php
class OtherClass
{
public function __construct(Errors $errorObject) {}
public function someMethod() {}
}
Then in my main controller..
Controller.php
<?php
class Main
{
public function __construct()
{
$this->errors = new Errors;
$this->other = new OtherClass($this->errors);
}
}
By doing this. I can add errors as I go to my error Object, across any objects i instantiate from the Main controller.
Now my code igniter version looks like this
/library/Errors.php
<?php
class Errors
{
public function __construct(){}
public function setError($msg){}
}
/library/OtherClass.php
<?php
class OtherClass
{
public function __construct(Errors $errorObject) {}
public function someMethod() {}
}
Then in my main controller..
Controller.php
<?php
class Main extends CI_Controller
{
public function __construct()
{
$this->load->library('Errors');
$this->load->library('OtherClass',$this->errors);
}
}
When I do this I get an error in my OtherClass saying that $errorObject is not an instance of Errors. Why is the object not being passed?
The problem is with $this->load->library which is defined like this.
public function library($library, $params = NULL, $object_name = NULL)
$params is expected to be an array. If it is not then the $params is set to NULL.
To get around this requires a bunch of monkey biz.
class Errors is unchanged but class OtherClass needs to be changed to...
class OtherClass
{
public function __construct($errorObject)
{
var_dump($errorObject[0]); //so we can prove it got passed
}
public function someMethod(){}
}
Note the removal of the type hint Error from the constructor declaration. Also, we access index 0 of the argument. The reason lies in what happens at the controller.
function __construct()
{
parent::__construct();
$this->load->library('Errors');
$this->load->library('OtherClass', [$this->errors]);
}
We have to put $this->error in an array so that load->library() won't mess with it.
The alternative is to not use "The CodeIgniter Way" and use good old fashion `new' instead. The controller then is...
function __construct()
{
parent::__construct();
$this->load->library('Errors');
$this->other = new OtherClass($this->errors);
}
And other class reverts to...
class OtherClass
{
public function __construct(Errors $errorObject)
{
var_dump($errorObject);
}
public function someMethod(){}
}
Now the problem is that without adding an autoloader to the system you wind up with
Fatal error: Class 'OtherClass' not found in ...
This LINK goes to a page that talks about the various ways to add an autoloader to CI. I know this has been answered on SO too. But I'm failing to find it at the moment.
Related
I have a problem like below:
I have an Interface name IBannerService
<?php
namespace App\Interfaces;
interface IBannerService
{
public function add($data);
public function list();
public function get($data);
public function delete($data);
}
and an instance name BannerService
class BannerService implements IBannerService
{
public function add($data)
{
return true;
}
public function list()
{
return true;
}
public function get($data)
{
return true;
}
public function delete($data)
{
return true;
}
public function test()
{
print_r("aaaa");
die();
}
}
finally I have a Controller name HomeController
class HomeController extends Controller
{
public function __construct(
IBannerService $bannerService
)
{
$this->bannerService = $bannerService;
}
public function index()
{
$listBanner = $this->bannerService->list();
$this->bannerService->test();
}
}
My configuration:
class DIServiceProvider extends ServiceProvider
{
$this->app->bind(
'App\Interfaces\IBannerService',
'App\Services\BannerService'
);
}
In app.php:
'providers'=>[
App\Providers\DIServiceProvider::class,
]
The code run well with $listBanner = true (just for testing).
The problem is:
Test Method was not declared in interface IBannerService but still go through and print out "aaa" the die.
Did I do something wrong?
Please suggest me, thank you!
That's perfectly normal functionality.
In the Laravel container you defined that when you ask for a IBannerService object, you want to get a BannerService class. And that is what you got. BannerService is an implementation of IBannerService, so no problem for the typehint.
A class is not limited to the functions defined by its interface so you can add as many other functions as you like. I wouldn't recommend it though, things like smart IDE's and phpstan would give you errors or warnings because to them, the variable is an implementation of IBannerService and this does not have a test() function.
If you really want to use more functions I would even recommend to use BannerService as the typehint. This way, static code analysis will still work.
Assume we have the following class (simplified):
class SuperConfig {
public $mainDir;
public function setDir() {
$this->mainDir = "path/to/dir";
}
}
This class is supposed to be extended in EVERY other class in the project, and I do need the setDir() function of the parent to be executed. Obviously, I could do it like this:
class A extends SuperConfig() {
public function __construct() {
parent::setDir();
}
// ... other stuff is about to be done ...
}
and I could access the properties in the child class like this:
class A extends SuperConfig {
public function doSomething() {
SuperConfig::mainDir;
}
}
This is a viable solution, but I got multiple hundreds of classes and doing this in every single one seems tedious. So, is there a way to do something like this:
class SuperConfig {
public $mainDir;
public function __extend() {
$this->setDir();
}
public function setDir() {
$this->mainDir = "path/to/dir";
}
}
__extend() obviously doesn't work like that, but I'm wondering is there is a trick how I could make this work.
class SuperConfig {
public $mainDir;
public function __construct() {
$this->setDir(); // consider moving setDir's code here as well,
// unless you have a good reason for it to be a method
}
public function setDir() {
$this->mainDir = "path/to/dir";
}
}
You simply do this, and then you expect all subclasses to call the parent constructor if they're overriding the constructor:
public function __construct() {
parent::__construct();
// more code
}
It's perfectly reasonable to expect children to call their parent constructor, unless they deliberately want to leave the instance in an unknown and potentially broken state.
Put the constructor in the class that is being extended.
class SuperConfig {
public $mainDir;
public function __construct() {
$this->setDir();
}
public function setDir() {
$this->mainDir = "path/to/dir";
}
}
In any class that extends SuperConfig, if they have a constructor also, be sure to include parent::__construct(); so that setDir is called.
Read deceze's answer for an actual solution to your problem.
I would like to point out tho, that you should not extend every class in your project from a Config class. There are several ways how you could improve that.
1.) Create a static config class which you simply can call everywhere without the need of creation
class SuperConfig {
protected static $mainDir = null;
public static function setMainDir($dir) {
self::$mainDir = $dir;
}
}
2.) Create a trait rather then a parenting class.
trait SuperConfig {
protected $mainDir = null;
public function setMainDir($dir) {
$this->mainDir = $dir;
}
}
which you then can use inside your classes:
class XYZ {
use SuperConfig;
public function doSomething() {
$this->setMainDir('path/to/your/dir/');
}
}
Note that you can do that in the constructor too (which is kinda what you want).
Im not saying those two solutions are the best, but I dont like the thought of extending all classes from a config class. Just does not make much sence. Just imagine that you can only extend from one class per time, while you can use as many traits as you wish (and also have as many static classes as you need).
Well, in this particular case you just need:
class SuperConfig {
public $mainDir = "path/to/dir";
}
I have view class like this:
class View {
public function __construct() {
}
public static function render($name) {
require 'views/user/header.php';
require 'views/user/'.$name.'.php';
require 'views/user/footer.php';
}
}
and I call the view class in controller like this:
class Controller {
function __construct() {
$this->view = new View();
}
}
and then I set the view property from controller child class, like this:
class Index extends Controller {
function __construct() {
parent::__construct();
$this->view->js = "test";
}
public function index() {
$this->view->render('index/index');
}
}
But when I want to get $this->js from "header.php" which is set at render function on view class, I always get this error message:
Fatal error: Uncaught Error: Using $this when not in object context
I was tried to check, Am I in the right class? using this methods in "header.php" file:
echo get_class(); // and this method return "View";
that means I was on the view class, right?
Can anyone please help me?
Thanks in advance
You have defined render() as a static method, but you are calling it as it was not static.
I would probably benefit from reading this: http://chadminick.com/articles/simple-php-template-engine.html
P.S. What you call "view" is just a template.
QUESTION :
I Cannot figure how to use classes extends in PHP... Even reading php.net website and some examples, there is something I cannot understand or missing !
Can you please tell me what I'm doing wrong ?
Api.php
class Api
{
public static $action = '';
# public function __construct()
# {
# }
public function actionCaller ($action,$args=NULL)
{
return self::$action_($args);
}
}
ApiForum.php
class ApiForum extends Api
{
#private static $forum;
public function __construct()
{
#self::$forum = new Api();
}
private function getPost ($args)
{
echo 'executed.';
#return "get forum post $args";
}
}
test.php
<?php
error_reporting(E_ALL);
ini_set('display_errors', true);
require_once('config.php');
require_once('classes/_Autoload_.php');
echo Api::actionCaller('forum')->getPost();
The result :
PHP Fatal error: Call to a member function getPost() on a non-object in /var/www/html/api.example.com/test.php on line 10
Please be clement with me ;)
CL
ANSWER:
Okay it's working now ! Thanks to all... There was more than one problem, here is the result :
Api.php
class Api
{
# public function __construct()
# {
# }
public function actionCaller ($action,$args=NULL)
{
return self::$action($args);
}
public function forum ()
{
return new ApiForum();
}
}
ApiForum.php
class ApiForum extends Api
{
# public function __construct()
# {
# }
public static function getPost ($args)
{
echo 'executed.';
}
}
test.php
error_reporting(E_ALL);
ini_set('display_errors', true);
require_once('config.php');
require_once('classes/_Autoload_.php');
echo Api::actionCaller('forum')->getPost('test');
I feel I need some more readings about classes and objects scopes ... :)
Just switch your "getPost" method declaration for this:
static function getPost($args){
A private method means only that class can execute that method. A static method means that it can be called without an object being instantiated, like what you're trying to do with the double colon eg. class::method(args).
Just for completeness sake, a public function is the middle ground. An object has to be instantiated for you to call it (via $object->method(args)), but it is available to any file that has imported that class
EDIT
Just a side note: I'd also like to add that for a method to be used as a static method, it still needs to be "included"! I apologise for the use of the word "imported", I've been playing around in a lot of other languages recently!
This question already has answers here:
Call to a member function on a non-object [duplicate]
(8 answers)
Closed 10 years ago.
I'm working on a small MVC framework in PHP for an exercise. PHP, however, doesn't seem to like my Controller class. The class contains an instance of a loader that loads views:
abstract class Controller
{
public $load;
function __construct($load)
{
$this->load = $load;
}
abstract public function index();
}
From there, I can override Controller for all my controllers. For instace, my index controller:
class Index extends Controller
{
public function index()
{
$this->load->view("hello_world");
}
}
But when I create it:
require 'Controller.php';
require 'Load.php'
require 'controllers/Index.php';
$i = new Index(new Load());
$i->index();
I get this error:
PHP Fatal error: Call to a member function view() on a non-object in /var/www/controllers/Index.php on line 7
Can you guys help me out? I know I set the load in the constructor, and the load class does have a method called view, so why is it giving me this error?
Also: Load class, just for good measure
class Load
{
public function view($filename, $data = null)
{
if(is_array($data)) extract($data);
include ROOT.DS.'views'.DS.$filename.'.php';
}
}
The problem is with this code, and it's not always obvious:
class Index extends Controller
^^^^^
{
public function index()
^^^^^
{
$this->load->view("hello_world");
}
}
This is the same name and therefore a PHP 4 backwards compatible constructor. The parent's constructor then is not called, $load not set and the function not defined.
Knowing this, there are many solutions, including:
namespace DelishusCake;
Introduce a Namespace
This automatically fixes your issue. You need to place this on top of the file.
class Index extends Controller
{
public function index($load = NULL)
{
isset($load) && $this->load = $load;
$this->load->view("hello_world");
}
}
Make the PHP4 backwards compatible constructor work
Or:
class MyIndex extends Controller
{
public function index()
{
$this->load->view("hello_world");
}
}
Rename the class
Or:
class Index extends Controller
{
public function __construct($load) {
parent::__construct($load);
}
public function index()
{
$this->load->view("hello_world");
}
}
Add a PHP 5 constructor, call the parent's constructor
Keep in mind that you only need this because it's the same name. The in depth description you can find as well in the PHP Manual on the Constructors and Destructors page.
You need to instantiate the parent class.
class Index extends Controller
{
public function __construct($load) {
parent::__construct($load);
}
public function index() {
$this->load->view("hello_world");
}
}