I would like to create a helper class for icon constants, but there is two types for every icon. I try to avoid duplication, so I make some inheritance.
As I understand the concept of constanst, this will never work, but I do not know what would be a better solution. Could you suggest a clean solution for this problem? The aim would be to avoid the need for remembering every constant. Thanks!
abstract class Heroicon
{
protected const PREFIX = "";
public const ACADEMIC_CAP = self::PREFIX . "academic-cap";
public const ADJUSTMENTS = self::PREFIX . "adjustments";
// ... +200 rows
}
class HeroiconSolid extends Heroicon
{
protected const PREFIX = "heroicon-s-";
}
class HeroiconOutline extends Heroicon
{
protected const PREFIX = "heroicon-o-";
}
echo HeroiconSolid::ACADEMIC_CAP; // "academic-cap" instead of "heroicon-s-academic-cap"
Related
I used to have several library classes with the exact same methods. Figured I'd learn a bit more about two important aspects of coding; Traits and DRY.
I have the following trait:
<?php
namespace App\Berry;
trait Lib
{
public function getIDs()
{
$oClass = new \ReflectionClass(get_called_class());
$aConstants = $oClass->getConstants();
foreach($aConstants as $sKey => $mValue)
{
if(!is_int($mValue))
{
unset($aConstants[$sKey]);
}
}
return array_values($aConstants);
}
}
The following class:
namespace App\Berry;
use Lib;
class postType
{
const POST_TYPE_BLOG_ID = 1;
const POST_TYPE_BLOG_LABEL = __('blog', 'lib');
const POST_TYPE_PAGE_ID = 2;
const POST_TYPE_PAGE_LABEL = __('page', 'lib');
const POST_TYPE_NEWS_ID = 3;
const POST_TYPE_NEWS_LABEL = __('news', 'lib');
}
And am calling it like this in my PicturesController class:
$cPostTypesLibrary = new postType();
$this->set('aPostTypes', $cPostTypesLibrary->getIDs());
Now to me, this seems almost exactly like the tell me to do in the docs example #4 (About using multiple traits)
The only difference I have is I have the use outside of my class due to getting cannot use class because it is not a trait
What am I missing here?
Your class is not using the trait, you are instead using the other use of the use keyword and trying to import the Lib class from the same namespace into, well, the same namespace.
To use traits correctly, go back to the documentation, and look at where they are placed. The use statement is placed inside of the class definition. In your case, it would look like this:
namespace App\Berry;
class postType
{
use Lib;
// ...
}
You have to declare the trait inside the class
class postType
{
use Lib;
}
I have a class wrapper, class car, that lives in the namespace of Automobile. Then I have class tire that lives in the namespace of Automobile\Wheels. In the tire class, I want to access a static variable from the car class, but the only way to do that is to reference the complete namespace\class.
<?php
namespace Automobile;
class Car {
public static $Axle;
public function __construct() {
static::$Axle = new Axle();
}
}
<?php
namespace Automobile\Wheels;
class Tire {
public $size;
public function __construct($size = 16) {
$this->size = $size;
\Automobile\Car::$Axle->add_wheel($this);
}
}
My questions are: is it an okay practice to reference classes like this? Are there any ways to reference the class without including the complete and absolute namespace for the class?
There's really no good or bad way to do it, although people might be quite opinionated about it (the same with using spaces or tabs for indents, but let's not go down that road ;) ). As long as they're properly referenced it's okay to use fully qualified class names.
You might want to use the use keywords to 'import' external classes, which improves the readability in my opinion:
<?php
namespace Automobile\Wheels;
use Automobile\Car;
class Tire {
public $size;
public function __construct($size = 16) {
$this->size = $size;
Car::$Axle->add_wheel($this);
}
}
You might want to reference the PHP documentation, as it is quite concise about the subject and gives good insight in how to use namespaces.
As others have pointed out, use use for the full namespaced class (e.g., use Automobile\Car;). From there, you can use the shortened class reference (e.g., Car) throughout the rest of the file.
As an aside, it's best practice to avoid statically invoking properties and method if at all possible. It's generally considered best practice to inject your dependencies as it decouples your code and allows for swapping out of pieces of your application using interfaces.
I would do something like this:
<?php
namespace Automobile;
class Car {
/** #var Axle */
public $axle;
public function __construct(Axle $axle) {
$this->axle = $axle;
}
}
<?php
namespace Automobile\Wheels;
use Automobile\Car;
class Tire {
public $size;
public function __construct(Car $car, $size = 16) {
$this->size = $size;
$car->axle->add_wheel($this);
}
}
I've been trying for a long time now to find a correct design using PHP to achieve what I want, but everything I've tried failed and I'm guessing it's probably because I'm not looking from the right angle, so I wish some of you can enlighten me and give me some good advice!
The design might seem a little weird at first, but I assure you it's not because I like to make things complicated. For the sake of simplicity I'm only giving the minimal structure of my problem and not the actual code. It starts with these:
<?php
// ------------------------------------------------------------------------
class Mother_A
{
const _override_1 = 'default';
protected static $_override_2 = array();
public static function method_a()
{
$c = get_called_class();
// Uses $c::_override_1 and $c::$_override_2
}
}
// ------------------------------------------------------------------------
class Mother_B extends Mother_A
{
public function method_b()
{
// Uses self::method_a()
}
}
Class Mother_A defines a static method that uses constants and statics to be overridden by children. This allows to define a generic method (equivalent of a "template" method) in the derived class Mother_B. Neither Mother_A or Mother_B are intended to be instanciated, but Mother_B should not be abstract. This exploits Late Static Binding, which I find very useful btw.
Now comes my problem. I want to define two classes, in n distinct 'situations' (situation 1, situation 2, etc):
<?php
// ------------------------------------------------------------------------
class Child_A_Situation_k extends Mother_A
{
// Uses method_a
}
// ------------------------------------------------------------------------
class Child_B_Situation_k extends Mother_B
{
// Uses method_a and method_b
}
Of course I'm not actually giving these stupid names; both classes have different names in each situation, but both follow the same derivation pattern from Mother_A and Mother_B. However, in each individual case ('situation'), both classes need the exact same constants/static override, and I don't know how to do that without duplicating the override manually in both classes.
I tried many things, but the closest I got was to implement an interface Interface_Situation_k that defined constants and statics for the situation k, and make both children implement this interface. Of course, you can't define statics in an interface, so it failed, but you get the idea. I would have traded the interface for a class, but then there's no multiple inheritance in PHP, so it's not valid either. :/ I'm really stuck, and I can't wait to read a possible solution! Thanks in advance!
this is the best i can do, i don't think there is a way to do it with less code.
Look at the comments inside the code for more info.
Fully working code:
<?php
class Mother_A
{
// you're using '_override_1' as a variable, so its obviously not a constant
// also i made it public for the setSituation function,
// you could keep it protected and use reflections to set it
// but i dont really see a reason for that.
// if you want that, look up how to set private/protected variables
public static $_override_1 = 'default';
public static $_override_2 = array();
public static function method_a()
{
$c = get_called_class();
var_dump($c::$_override_1);
var_dump($c::$_override_2);
// Uses $c::_override_1 and $c::$_override_2
}
public static function setSituation($className)
{
$c = get_called_class();
// iterate through the static properties of $className and $c
// and when the you find properties with the same name, set them
$rBase = new ReflectionClass($c);
$rSituation = new ReflectionClass($className);
$staBase = $rBase->getStaticProperties();
$staSituation = $rSituation->getStaticProperties();
foreach($staSituation as $name => $value)
{
if(isset($staBase[$name])) $c::$$name = $value;
}
}
}
// ------------------------------------------------------------------------
class Mother_B extends Mother_A
{
public function method_b()
{
self::method_a();
}
}
class Situation_k
{
public static $_override_1 = 'k';
public static $_override_2 = array('k','k');
}
class Child_A_Situation_k extends Mother_A { }
Child_A_Situation_k::setSituation('Situation_k');
// This is not as short as writing 'extends Mother_A, Situation_k'
// but i think you wont get it shorter
class Child_B_Situation_k extends Mother_B { }
Child_B_Situation_k::setSituation('Situation_k');
echo '<pre>';
Child_A_Situation_k::method_a();
echo "\n";
Child_B_Situation_k::method_a();
echo "\n";
Child_B_Situation_k::method_b();
echo "\n";
echo '</pre>';
?>
Am I correct in assuming that const properties are automatically public? Is there a way to make them private or protected?
Thanks in advance.
Historically, class constants were always publicly accessible as long as the class was loaded and there was no way to change this.
As of PHP 7.1, they remain public by default but access modifiers may now be applied. Here's the example from the release notes:
<?php
class ConstDemo
{
const PUBLIC_CONST_A = 1;
public const PUBLIC_CONST_B = 2;
protected const PROTECTED_CONST = 3;
private const PRIVATE_CONST = 4;
}
Class constants should have the option of being private/protected because being public exposes internal details of the class that other classes/code can mistakingly use thinking they are ok to use because they are public.
It would be nice to know that changing a private constant would ONLY affect the class it's defined in. Unfortunately we don't have that option.
Remember back to when you were learning Object Design & Analysis... you give class methods and attributes the most RESTRICTIVE access possible, and later relax them as needed (much harder to go back the other way because other classes/code start using them which would then break other code).
WORKAROUND
Best bet is to just create a private or protected variable and upper-case it to show it's a constant. You could always create a class called constant($value_to_be_constant) that implements the correct magic methods / spl interfaces to prevent it from being changed.
I am aware this question is 6 years old
Php 7.1 (currently RC1) allows to specify visibility on class constants.
class Token {
// Constants default to public
const PUBLIC_CONST = 0;
// Constants then also can have a defined visibility
private const PRIVATE_CONST = 0;
protected const PROTECTED_CONST = 0;
public const PUBLIC_CONST_TWO = 0;
//Constants can only have one visibility declaration list
private const FOO = 1, BAR = 2;
}
Additional info
RFC on class const visibility
Blog post on Class Constant Visibility
As of php7.1, you can define your class constants with access modifiers (public, private or protected). Have a look at the following example:
<?php
class superheroes{
public const kal_el = 'Superman';
protected const bruce_wayne = 'Batman'; # works php7.1 onwards
private const anthony_stark = 'Iron Man'; # works php7.1 onwards
public static function show_remaining(){
echo self::bruce_wayne, '<br />';
echo self::anthony_stark, '<br />';
}
}
echo superheroes::kal_el, '<br />';
superheroes::show_remaining();
Credits: http://dwellupper.io/post/48/defining-class-constants-in-php
i have a config class which is an abstract class. I would like to set it up so it automatically detects which server the site is on and then assigns appropriate constants. I get an error on line ten $this->hostName = $_SERVER['SERVER_NAME']; expecting `T_FUNCTION.
What is the correct way to do this and is there a better way to do this? Here is the first part of my class
abstract class config{
public $hostName;
public $hostSlices;
public $domain;
echo $_SERVER['SERVER_NAME'];
//strips out the "www" from the server name but only if it has the name on it .
$this->hostName = $_SERVER['SERVER_NAME'];
$this->hostSlices = explode(".",$this->hostName);
if($this->hostSlices[0]=="www"){array_shift($this->hostSlices);}
$this->domain = join(".",$this->hostSlices);
//depending on which domain is used, different config setup is used.
switch ($this->domain){
case "localhost":*/
const HOST = "localhost";//would http://localhost/ work as well. In that case, then this and the SITE_ROOT could be the same variable and i would preferentially set them depending on the host that the site is on.
const USER = "root";
const PWD = "xxxxxxx";
const NAME = "hurunuitconz";//database name
//public $name = "hello from the config class";//you cannot access variables from an abstract class you should define constants and then the can be used anywhere
###########Location of file groups########
const SITE_ROOT = "http://localhost";
const ADMIN_IMAGES = 'http://localhost/images/user_images/admin_images';
break;
case "charles.hughs.natcoll.net.nz":
const HOST = "charles.hughs.natcoll.net.nz";//would http://localhost/ work as well. In that case, then this and the SITE_ROOT could be the same variable and i would preferentially set them depending on the host that the site is on.
const USER = "charles_andrew";
const PWD = "xxxxxxx";
const NAME = "charles_hurunuitconz";//database name
###########Location of file groups########
const SITE_ROOT = "http://charles.hughs.natcoll.net.nz/_Assignments/Industry/www";//this is just confusing the way natcoll makes us do this.
const ADMIN_IMAGES = 'http://charles.hughs.natcoll.net.nz/_Assignments/Industry/www/images/user_images/admin_images';
break;
}
An abstract class shouldn't allow you to set private data (only an inherited concrete class).
Also, take a look at this link in stackoverflow for an interesting discussion on SERVER_NAME vs. HTTP_HOST
You have to wrap all that code in a constructor or better yet a function called init() or somthing. Then when you override init in the child classes you would call parent::init(). Did you mean to make this class static, not abstract?
The way you're structuring your code is just not right. PHP does allow for many crazy things, but defining constants and outputting code as a part of an abstract class makes no sense whatsoever in terms of OOP.
What you probably mean to do is to have a helper class that defines configuration settings based upon the local server name. To do so, you have a couple options:
Option 1) Create a regular class with a constructor.
class Config {
public $x;
public $y;
public function __construct() {
switch (...) {
$this->x = 2;
$this->y = 3;
}
}
}
And use it like this:
$config = new Config();
echo "Variable y: " . $config->y;
Option 2) Static method in an abstract class.
abstract class Config {
public $boolInitialized = false;
public static function init() {
if (self::$boolInitialized) {
return;
}
self::$boolInitialized = true;
switch (...) {
self::$x = 1;
self::$y = 2;
}
}
public static function getX() {
self::init();
return self::$x;
}
}
And use it like this:
echo Config::getX();
You cant output something in a class without declaring first a method
this will fail
abstract class fo
{
public $fo;
echo $fo;
}
**but this will work**
abstract class fo
{
public $fo;
public function __construct()
{
$this->fo = $_SERVER['SERVER_NAME'];
}
public function sayFo()
{
echo $this->fo;
}
}