I am geting a headache with php "Strict Standards" notices in a class:
Parent class:
class View {
/**
*
* #param string $viewFolder
* #param string $viewBasename
* #return boolean
*/
public static function exists($viewFolder, $viewBasename){
$exists = false;
if(\is_string($viewFolder) && \is_string($viewBasename)){
$exists = \is_file(\APPLICATION_PATH."View/$viewFolder/$viewBasename.phtml");
}
return $exists;
}
/**
*
* #param string $viewFolder
* #param string $viewBasename
* #param array $viewVariables
*/
public static function load($viewFolder, $viewBasename,array $viewVariables = []){
extract($viewVariables);
require \APPLICATION_PATH."View/$viewFolder/$viewBasename.phtml";
}
}
Child class:
class Layout extends View{
/**
*
* #param string $viewBasename
*/
public static function exists($viewBasename) {
return parent::exists('_layout', $viewBasename);
}
/**
*
* #param string $viewBasename
* #param array $viewVariables
*/
public static function load($viewBasename, array $viewVariables = array()) {
parent::load('_layout', $viewBasename, $viewVariables);
}
}
I've read this topic and now its clear that the reason are those missing parameters in the child class methods.
Declaration of Methods should be Compatible with Parent Methods in PHP
Is there a way of getting rid of these notices without disabling error reporting, or maybe a better approach of doing this?
Thanks in advance.
The better approach is to write your classes in a clean and sensible way. In terms of OOP practice, your child classes that need to extend the parent's methods should redefine them in the same format (hence your warning from PHP).
In your example, your general workflow for the exists() method implementation appears to be the following:
Parent class has an exists method with a folder and a filename
Child class is cutting corners, because it knows its folder already, and only accepts a filename
Child class passes a predefined variable to the parent method
If you look at this objectively, your goal is that the view should be able to call the exists() method on a Layout class and only pass the one parameter, so then you ask "how can I remove the requirement to pass the folder?" Here are a couple of options:
1: Pass the folder name in as the second argument, and making it optional in the Layout (child) class's implementation:
# Class: Layout
/**
* #param string $viewBasename
* #param string $viewFolder
*/
public static function exists($viewBasename, $viewFolder = '_layout') {
return parent::exists($viewBasename, $viewFolder);
}
# Class: View
public static function exists($viewBasename, $viewFolder) {
// essentially you swap around the order of the params
}
2: Don't pass in the folder at all, but use a class property in the child and take advantage of late static bindings:
# Class: Layout
/**
* Define the folder for your layouts
* #var string
*/
const VIEW_FOLDER = '_layout';
The exists() implementation stays the same as in your example currently.
# Class: View
public static function exists($viewBasename) {
// Get your folder from a child instead of an argument
$viewFolder = static::VIEW_FOLDER;
$exists = false;
if(\is_string($viewFolder) && \is_string($viewBasename)){
$exists = \is_file(\APPLICATION_PATH."View/$viewFolder/$viewBasename.phtml");
}
return $exists;
}
A note here, you could also use a function in place of the constant, either abstract in the View class or not, e.g.:
# Class: View
abstract class View {
/**
* This method should be defined in children to provide the layout name.
* Using an abstract method would ensure that it is defined by children,
* however if View is going to be used on its own then do not use this approach.
* #return string The view's folder name
*/
abstract protected static function getViewFolder();
public static function exists($viewBasename) {
// Get view folder from the children (same as the constant example)
$viewFolder = static::getViewFolder();
// ...
}
}
# Class: Layout
class Layout extends View {
protected static function getViewFolder() {
return '_layout';
}
public static function exists($viewBasename) {
return parent::exists($viewBasename);
}
}
To be honest, the constant option is a little shorter and they both essentially do the same thing, other than if you use a function instead of a constant you would be able to define manipulation logic if required.
If I were you, I'd use a class constant for the view folder and take it out as an argument. You'd then implement the static::VIEW_FOLDER in place of the argument passed into load and exists.
Related
What is the canonical #return type for a static method of a parent class that returns an instance of a subclass, undetermined at time of writing? For example:
<?php
class Thing {
/**
* Do something
*
* #return ??? Instance of some new class.
*/
public static function create_subclass($class) {
return new $class();
}
}
class Person extends Thing {}
$person = Thing::create_subclass('Person');
?>
It seems like this shouldn't be #return self because it's returning a subclass of self, and specifying all possible return classes would be silly. Any ideas?
You can use #return Thing or #return Person|AnotherClass|AnotherClass
I just implemented namespaces in my small application as outlined here: http://www.yiiframework.com/doc/guide/1.1/en/basics.namespace
I'm running into an issue where my controller will no longer access Yii::app()->getRequest(); saying it can't find include(C:\Users\bkuhl\htdocs\instaLabel\application\protected\components\Yii.php): failed to open stream: No such file or directory.
I realize that's because I declared the namespace as application/components. But I'm not sure how to work around this one...
<?php
namespace application\components;
/**
* Controller is the customized base controller class.
* All controller classes for this application should extend from this base class.
*/
class Controller extends \CController {
/* #var $request CHttpRequest */
protected $request = null;
/**
* #var string the default layout for the controller view. Defaults to '//layouts/column1',
* meaning using a single column layout. See 'protected/views/layouts/column1.php'.
*/
public $layout='//layouts/column1';
/**
* #var array context menu items. This property will be assigned to {#link CMenu::items}.
*/
public $menu=array();
/**
* #var array the breadcrumbs of the current page. The value of this property will
* be assigned to {#link CBreadcrumbs::links}. Please refer to {#link CBreadcrumbs::links}
* for more details on how to specify this property.
*/
public $breadcrumbs=array();
public function __construct ($id, $module = null) {
parent::__construct($id, $module);
$this->request = Yii::app()->getRequest();
}
You need to fully qualify the relative class name Yii.
The most convenient way to do this is by importing the class: just add use Yii; below your namespace declaration.
Have you tried:
$this->request = \Yii::app()->getRequest();
\ will use the global namespace:
Prefixing a name with \ will specify that the name is required from the global space even in the context of the namespace.
Is it possible to get the base object from derived object in php
Something like this
class base {}
class derived extends base{
public function getBase()
{
return (base)$this;
}
The above code, throws out a error
You can use parent:: to resolve to a parent method, property or constant.
If you're trying to get the name of the base class, here's a way to do that:
class base {
public function getClassName() {
return "base";
}
}
class derived extends base{
public function getBaseClassName() {
return parent::getClassName();
}
public function getClassName() {
return "derived";
}
}
$d = new derived();
echo $d->getBaseClassName();
Edit: When you use inheritance to extend a class (eg: derived extends base), you are saying that derived is a kind of base, and all instances of derived are also instances of base. In most OO languages, the two instances, base and derived are not separate entities, and they can not be treated separately. (C++ is an exception to the rule in this regard).
If you need to treat the instances separately, then inheritance is the wrong tool for the job. Use extension by containment, rather than inheritance. This will look something like the following:
class base {
public someBaseFunction() {
// ...
}
}
class derived {
/**
* each instance of `derived` *contains* an in instance of `base`
* that instance will be stored in this protected property
*/
protected $base;
/**
* constructor
*/
function __construct() {
// remember to call base's constructor
// passing along whatever parameters (if any) are needed
$this->base = new base();
// ... now do your constructor logic, if any ...
}
/**
* Here's the method that fetches the contained
* instance of `base`
*/
public function getBase() {
return $this->base;
}
/**
* If needed, `derived` can implement public elements
* from `base`'s interface. The logic can either delegate
* directly to the contained instance of base, or it can
* do something specific to `derived`, thus "overriding"
* `base`'s implementation.
*/
public function someBaseFunction() {
return $this->base->someBaseFunction();
}
/**
* of course, `derived` can implement its own public
* interface as well...
*/
public function someOtherFunction() {
}
}
I've built a list rendering class:
class ListRenderer
{
/**
* #param int $columns number of columns
* #param string $element container element
* #param string $styleClass container style
*/
public function __construct($columns,$element='div',$styleClass=''){...}
...
/**
* #param mixed $callback function to render items - should take two
* parameters ($item,$index)
* #param array $list items to render
*/
public function renderArrayList($callback,$list){...}
/**
* #param mixed $callback function to render items - should take 3 parameters
* ($row,$i,$count) $i and $count are the position and total items
* #param string $sql query string
* #param string $errorMessage
* #param int $blanks number of blank items to render. The callback will be
* invoked with a null $row parameter for the blank records.
*/
public function renderQueryList($callback,$sql,$errorMessage,$blanks=0){...}
...
}
The callback function renders a single item.
This could also be accomplished using templates:
class ListRenderer
{
...
//$itemRenderer implements ListItemRenderer
public function renderArrayList($itemRenderer,$list){...}
//$itemRenderer implements ListItemRenderer
public function renderQueryList($itemRenderer,$sql,$errorMessage,$blanks=0){...}
...
}
template ListItemRenderer
{
public function renderArrayItem($item,$index);
public function renderQueryItem($row,$index,$count);
}
class SomeClass implements ListItemRenderer
{
...
public function renderArrayItem($item,$index){...}
public function renderQueryItem($row,$index,$count){...}
...
}
I'm not sure why I went with callbacks on this one; coming from a Java background I would normally be inclined to use the second approach.
It seems to me that:
Callbacks are more flexible
Templates would limit a single class to one renderArrayItem function, for example, where callbacks would allow use of multiple functions per class for that purpose.
The template approach requires the function to be a class member.
Callbacks tend to produce less maintainable code.
Are there any strong reasons to go one way or the other on this?
There can be multiple reasons for the one versus the other and the other way round. Especially for your case I have no clue what the difference is because I don't know your application.
So I ask back: Why one versus the other? If you still don't know which way to go, or unsure if you want the one or the other explicitly, why don't you make a callback variant you can use when needed? You can inject the callbacks when instantiating the class:
class ListItemCallbackRenderer implements ListItemRenderer
{
private $callbacks;
public function __construct(array $callbacks)
{
$this->callbacks = $callbacks;
}
public function renderArrayItem($item,$index)
{
$callback = $this->callbacks[__FUNCTION__];
// ...
}
public function renderQueryItem($row,$index,$count)
{
$callback = $this->callbacks[__FUNCTION__];
// ...
}
}
Done this, the interface stays the same which makes your overall design more fluid and you can decide which variant to use everywhere in your application. No need to degrade yourself to one method actually.
The following is code for Base.php and the class Base
/**
* #return ?
*/
public static function withId($id, $class = __CLASS__)
{
$instance = new $class($id);
if ($instance->getId() == self::ID_OF_UNKNOWN_USER) {
$instance = null;
}
return $instance;
}
Other classes will extend this class. I'm using late static binding to figure out who should be created prior to calling withId() and passing the class name as $class.
The returned class could be Base or any of its children. How do I mark that in phpDoc?
This looks somewhat like a factory pattern, in which case the user code shouldn't know the concrete class returned. Usually you'd use an abstract class or interface and program for that. In this case:
/**
* #return null|Base
*/
There's no way to use values generated at runtime in docstrings (which are static).
The returned class could be Base or any of its children. How do I mark that in phpDoc?
Straight forward.
/**
* #return Base
*/