I have the following snippet of code.
abstract class MrParent {
public function __construct() {
$this->var = 'a';
}
}
class MrChild extends MrParent {
public function hello() {
echo 'Hello';
}
}
$MrGuy = new MrChild();
Now, in PhpStorm, when I middle-click ("Go To Declaration") on the last line of the "MrChild" class, the cursor jumps up to the "__construct" line. I was expecting it to go to the "class MrChild extends MrParent" line.
In a single document, this is OK, but in a setup where it's one class per file, this is quite annoying because it means the IDE is constantly showing me the class I don't want.
I know that if I added the following code to the "MrChild" class, I'd get what I want, but that seems like I shouldn't be fixing what I consider to be an IDE bug by adding extra code.
public function __construct() {
parent::__construct();
}
Do you have any suggestion?
You are facing WI-4880 issue. Feel free to watch/vote.
Related
I want to call a static method from a variabe class in PHP. As pointed out several times on SO and because it is general practice, the following works as expected:
class Foo {
public function compile($strClass) {
$strClass::find(); // this works
}
}
Nonetheless I have to call different find methods from $strClass from different methods of a class Foo. That is, why I want to store $strClass in $this->strClass. Unfortunately, this doesn't work:
class Foo {
protected $strClass;
public function __construct($strClass)
{
$this->strClass = $strClass;
}
public function compile($strClass) {
$this->strClass::find(); // this does not work
}
}
Any idea or hint on how to solve that issue?
Update:
As pointed out in the comments, it might be a solution to use call_user_func like this:
call_user_func(array($this->strClass, 'find'), $strParam);
Anyhow, this makes code completion in PHPstorm impossible. Any hints on that? Maybe using code annotation?
You can change your compile method to this:
public function compile($strClass) {
call_user_func(array($this->strClass, 'find'));
}
This class design is flawed. I would try to get rid of the static methods completely, but here is a solution that exploits the fact that you can call static methods on objects:
class Foo {
protected $strClass;
public function __construct($strClass)
{
$this->strClass = new $strClass;
}
public function compile($strClass) {
$this->strClass::find();
}
}
UPDATE: nevermind, this is a syntax error in all current PHP versions, you actually have to do it like this:
$strClass = $this->strClass;
$strClass::find();
And this works with your original code as well, where $this->strClass is a string.
First: I've already reviewed all the questions I can find about __destruct() and CodeIgniter, and none seem to address the same issue I'm seeing.
Right. That aside. I'll show code first, as the problem will make more sense after reading. (N.B.: some code redacted, but nothing important to the question. The Billing::index function however, is comprises only one space character in production as well as here)
application/core/MY_Controller.php
class MY_Controller extends CI_Controller{
public $view = '';
public $data = array();
public $template = '';
function __destruct(){
if(!is_null($this->template) && ($this->template == '')){
$this->template = 'public';
}
if($this->view == ''){
$this->view = $this->uri->segment(1,'index').'/'.$this->uri->segment(2,'index');
}
if(!is_null($this->template)){
echo $this->load->view('templates/'.$this->template.'/top',$this->data,true);
}
echo $this->load->view('views/'.$this->view,$this->data,true);
if(!is_null($this->template)){
echo $this->load->view('templates/'.$this->template.'/bottom',$this->data,true);
}
}
}
class MY_ProtectedController extends MY_Controller{
function __destruct(){
parent::__destruct();
}
}
application/controllers/billing.php
class Billing extends MY_ProtectedController{
public function index(){ }
}
This, loads perfectly. The main "billing" page is just HTML, so the destructor fires the right template and view.
However, I would love to remove the redundant public function index(){ }, as it really does nothing.
So if I remove that from application/controllers/billing.php so that it's just
class Billing extends MY_ProtectedController{ }
and visit /billing, I get the output I want, however, I get it twice.
If I do some test echos, MY_ProtectedController::__destruct() is called once, but MY_Controller::__destruct is called twice.
My question is: why is this the case, and can it be stopped?
I'm not super familiar with CI core, and I figure that by the time I dig far enough to find the controller instantiation, someone here has probably already got an answer. Will of course update this if my own digging turns anything up.
Solution ended up being fairly simple - but credit for spotting this goes to one of our other programmers.
If I put some code in my 404 route to prevent the auto-destructor from running, I only get one set of output.
class Index extends MY_PublicController {
public function not_found(){
$this->cancel_destruct = TRUE;
}
}
class MY_Controller extends CI_Controller{
$this->cancel_destruct = FALSE;
function __destruct(){
if(!$this->cancel_destruct){
[...]
}
}
}
Second update
I think I've been approaching this problem from the wrong side of the coin. Would I be correct in assuming that I should be making 'First' an abstract class and just finding a way to reference 'Second' and 'Third' at a later time?
Update
Based on some of the feedback, I have added some content to try and clear up what I would like to do. Something similar to this effect.
I know from just looking at the code below that, it is a waste of performance "if" it did work and because it doesn't, know I am approaching the problem from the wrong angle.The end objective isn't all to uncommon at a guess from some of the frameworks I've used.
I'm more trying to base this particular bit of code on the CodeIgniter approach where you can define (what below) is STR_CLASS_NAME in a config file and then at any point through the operation of the program, use it as i have dictated.
STR_CLASS_NAME = 'Second';
class First {
protected $intTestOne = 100;
public function __construct() {
$strClassName = STR_CLASS_NAME;
return new $strClassName();
}
public function TestOne() {
echo $this->intTestOne;
}
protected function TestThreePart() {
return '*Drum ';
}
}
class Second extends First{
/* Override value to know it's working */
protected $intTestOne = 200;
/* Overriding construct to avoid infinite loop */
public function __construct() {}
public function TestTwo() {
echo 'Using method from extended class';
}
public function TestThree() {
echo $this->TestThreePart().'roll*';
}
}
$Test = new First();
$Test->TestOne(); <-- Should echo 200.
$Test->TestTwo(); <-- Should echo 'Using method from extended class'
$Test->TestThree(); <-- Should echo '*Drum roll*'
You may be asking, why do this and not just instantiate Second, well, there are cases when it is slightly different:
STR_CLASS_NAME = 'Third';
class Third extends First{
/* Override value to know it's working */
protected $intTestOne = 300;
/* Overriding construct to avoid infinite loop */
public function __construct() {}
public function TestTwo() {
echo 'Using method from extended class';
}
public function TestThree() {
echo $this->TestThreePart().'snare*';
}
}
$Test = new First();
$Test->TestOne(); <-- Should echo 300.
$Test->TestTwo(); <-- Should echo 'Using method from extended class'
$Test->TestThree(); <-- Should echo '*Drum snare*'
Situation
I have a an abstract class which extends a base class with the actually implementation; in this case a basic DB wrapper.
class DBConnector ()
class DBConnectorMySQLi extends DBConnector()
As you can see, MySQLi is the implementation. Now, dependant upon a value in the configuration process, a constant becomes the class name I wish to use which in this case (as shown below builds DBConnectorMySQLi.
define('STR_DB_INTERFACE', 'MySQLi');
define('DB_CLASS', 'DBConnector'.STR_DB_INTERFACE);
Objective
To have a base class that can be extended to include the implementation
For the code itself not to need know what the name of the implementation actually is
To (in this case) be able to type or use a project accepted common variable to create DBConnectorMySQLi. I.E. $db or something similar. W
Issue
When it comes to actually calling this class, I would like the code to be shown as below. I was wondering whether this is at all possible without the need to add any extra syntax. On a side note, this constant is 100% guaranteed to be defined.
$DBI = new DB_CLASS();
Solution 1
I know it is possible to use a reflection class ( as discussed in THIS QUESTION) and this works via:
$DBI = new ReflectionClass(DB_CLASS);
However, this creates code that is "dirtier" than intended
Solution 2
Start the specific implementation of DBConnectorMySQLi within the constructor function of DBConnector.
define('STR_DB_INTERFACE', 'MySQLi');
define('DB_CLASS', 'DBConnector'.STR_DB_INTERFACE);
class DBConnector() { public function __construct() { $this->objInterface = new DBConnectorMySQLi(); }
class DBConnectorMySQLi()
This however would result in the need to keep on "pushing" variables from one to the other
Any advice is much appreciate
You can use variables when you instantiate a class.
$classname = DB_CLASS;
$DBI = new $classname();
Source: instantiate a class from a variable in PHP?
strange problem here. Using Yii framework I have the following class
class HtmlTableUi
{
public function __construct(CWebModule &$module,$data,$actions)
{
//...code goes here...
}
protected function renderTable()
{
//... code goes here ...
}
}
I can call HtmlTableUi::renderTable() from instance of HtmlTableUi in SchedulerModule class which is the main class in a separate application module. My SchedulerModule.php file:
<?php
Yii::import('scheduler.components.HtmlTableUi');
class SchedulerModule extends CWebModule
{
public function init()
{
parent::init();
}
public function beforeControllerAction($controller, $action)
{
return true;
}
public function printUI($data,$actions,$submitPath)
{
$ui = new HtmlTableUi($this,$data,$actions,$submitPath);
$ui->renderTable();
}
}
Here comes the tricky part - when I call SchudulerModule::printUI from view (index.php) this way
<?php
$this->module->printUI($casino,$actions,null);
?>
the code flow goes through SchudulerModule::printUI until reaching the point where renderTable is invoked ($ui->renderTable();) and instead stepping at the first line inside the body of that method it, contrary to any logic and rules, surprisingly jumps to CHttpSession::close !!?
Notice the Call Stack before invoking renderTable
protected/modules/scheduler/SchedulerModule.php.SchedulerModule->printUI:153
protected/modules/scheduler/views/default/index.php.require:21
/home/default/workspace/src/vlt/web/yii-1.1.12.b600af/framework/web/CBaseController.php.CBaseController->renderInternal:127
/home/default/workspace/src/vlt/web/yii-1.1.12.b600af/framework/web/CBaseController.php.CBaseController->renderFile:96
/home/default/workspace/src/vlt/web/yii-1.1.12.b600af/framework/web/CController.php.CController->renderPartial:870
/home/default/workspace/src/vlt/web/yii-1.1.12.b600af/framework/web/CController.php.CController->render:783
protected/modules/scheduler/controllers/DefaultController.php.DefaultController->actionIndex:57
and after:
/home/default/workspace/src/vlt/web/yii-1.1.12.b600af/framework/web/CHttpSession.php.CHttpSession->close:134
/home/default/workspace/src/vlt/web/yii-1.1.12.b600af/framework/web/CHttpSession.php.SchedulerModule->printUI:0
protected/modules/scheduler/views/default/index.php.require:21
/home/default/workspace/src/vlt/web/yii-1.1.12.b600af/framework/web/CBaseController.php.CBaseController->renderInternal:127
/home/default/workspace/src/vlt/web/yii-1.1.12.b600af/framework/web/CBaseController.php.CBaseController->renderFile:96
/home/default/workspace/src/vlt/web/yii-1.1.12.b600af/framework/web/CController.php.CController->renderPartial:870
/home/default/workspace/src/vlt/web/yii-1.1.12.b600af/framework/web/CController.php.CController->render:783
protected/modules/scheduler/controllers/DefaultController.php.DefaultController->actionIndex:57
Has anyone had similar issue? Can anyone explain that behavior?
I found the solution!!! The access modifier of renderTable is supposed to be public, not protected. This wrong somehow in Yii framework results in unexpected runtime behavior instead of compilation error.
This is the first question I ask from many others to come.
Someone here might call me crazy because I'm following the mentioned book in the question's Title using PHP-OO.
At the first chapter, the authors introduce a simple project called 'SimUDuck' and, although I've reproduced the same in Java, I was wondering to reproduce the same using PHP.
At the end, the SimUDuck's project creates two (2) interfaces (FlyBehavior and QuackBehavior), more than five (5) classes implementing those interfaces (e.g. FlyWithWings(), Quack() etc), an abstract class called Duck and three (3) or four (4) different ducks species classes extending Duck (Mallard, HeadRedDuck, RubberDuck etc), just to demonstrate how important is to program for interface.
To simulate the Java main method environment, I've created a PHP class called MiniDuckSimulator, including the function 'public static function main()' and in the same script I've added "MiniDuckSimulator::main();". The script works without errors.
The intriguing issue is that without call any QuackBehavior::quack() implemented method, the echo 'Quack!quack!' appears. Those who has read this book knows what I'm talking about.
Note: Below is a particular part of the script:
interface FlyBehavior {
public function fly();
}
interface QuackBehavior {
public function quack();
}
include_once 'FlyBehavior.php';
class FlyWithWings implements FlyBehavior {
public function fly() {
echo 'I'm flying!<br />';
}
}
include_once 'QuackBehavior.php';
class Quack implements QuackBehavior {
public function quack() {
echo 'Quack!<br />';
}
}
abstract class Duck {
protected $flyBehavior;
protected $quackBehavior;
function __construct() {
}
public function performFly(){
$this->flyBehavior->fly();
}
public function performQuack(){
$this->quackBehavior->quack();
}
public function setFlyBehavior($flyBehavior){
$this->flyBehavior = $flyBehavior;
}
public function swim(){
echo "All the ducks float, including the decoy!<br />";
}
}
include_once 'Duck.php';
include_once 'FlyWithWings.php';
include_once 'Quack.php';
class Mallard extends Duck {
function __construct() {
$this->flyBehavior = new FlyWithWings();
$this->quackBehavior = new Quack();
}
}
class MiniDuckSimulator {
public static function main(){
$mallard = new Mallard();
$mallard->performFly();
}
}
MiniDuckSimulator::main();
Thanks in advance.
LucDaher.
The reason you are seeing Quack!<br /> output is because of this:
class Quack implements QuackBehavior {
public function quack() {
echo 'Quack!<br />';
}
}
Here's your problem: If you simply run new Quack(); the quack() method is automatically being executed by php as a constructor because it is the same name as your class. -- I see you referenced Java in your question, so this shouldn't be a foreign concept to you.
new Quack(); // => Quack!<br />
A potentially better way
<?php
interface CanFly {
public function fly();
}
interface CanQuack {
public function quack();
}
abstract class Duck implements CanFly, CanQuack {
protected $color = "DEFAULT"
public function fly(){
echo "I'm flying with my {$this->color} wings\n";
}
public function quack(){
echo "I'm quacking\n";
}
public function swim(){
echo "I'm swimming\n";
}
}
class Mallard extends Duck {
public function __construct(){
$this->color = "green";
}
public function quack(){
echo "My quack sounds more like a honk\n";
}
}
$m = new Mallard();
$m->fly();
$m->quack();
$m->swim();
?>
Output
I'm flying with my green wings
My quack sounds more like a honk
I'm swimming
In your situation, I would personally assume that I've overlooked something when saying that an echo is being reached in the code without a call to that method. I can't see a way that this would be possible.
Reanalyze your code and look for a sneaky way that your echo 'Quack!quack!' is being reached.
Comment this line:
echo 'Quack!<br />';
Do you see any more quacks? If so, then there is an echo/exit/die in your code with this string!
It is because when you have the class with the same name of the method, the PHP consider it as a constructor. This is already deprecated in php 7 and it will be discontinued soon. You can see it on the online documentation: http://php.net/manual/en/migration70.deprecated.php