I am in the process of re educating myself in the programming of OOP
in PHP.
I have been under the erroneous assumption that all variables not static inside a class def
had to be proceeded with $this->. I finally stumbled over myself with a variable naming collision
and am now relearning, somewhat, the way of php OOP.
My present question is:
Can you mark a method property set with $this-> as public, private, or protected?
I have done;
class _DEMO
{
private $this->someVar = 'whatever';
}
and I get a syntax error.
then:
class _DEMO
{
public function __construct($_ip)
{
$this->ip = $_ip; // <<< how can I set access on this property?
}
}
As of now I don't know how to use properties with access levels set
other than to declare static properties.
OK, so I tried
class _DEMO
{
public $_someVar = 'so and so';
}
$_a = new _DEMO()
print $_a->someVar // NADA
So, I take it that the variable can be declared this way but not initialized?
on second thought, OOPs! I saw the problem with variable reference $_testy and $this->testy, should be $this->_testy
class _DEMO
{
private static $_doTell = 'Well??...';
private $_testy = "So What? ";
public function __construct($_ip)
{
$this->_testy .= " right Now?";
$this->ip = $_ip;
$this->soWhat = 'Boo!...';
}
public function getVar($_var, $_addon)
{
$this->setVar($_var, $_addon);
switch($_var)
{
case 'soWhat':
return $this->soWhat;
break;
case 'ip':
return $this->ip;
break;
case 'doTell':
return self::$_doTell;
break;
default:
break;
}
}
private function setVar($_var, $_input)
{
switch($_var)
{
case 'soWhat':
$this->soWhat .= $_input;
break;
case 'ip':
$this->ip .= $_input;
break;
case 'doTell':
self::$_doTell .= $_input;
break;
default:
break;
}
}
}
$_test = new _DEMO('Hello??...');
print "Test result 1: ".$_test->ip;
print "<br>Test result 2: ".$_test->getVar('doTell', ' So, how old are you??');
print "<br>Test result 3: \$_test::\$_doTell; Fatal error: Cannot access private property _DEMO::$_doTell";
print "<br>Test result 4: ".$_test->testy; // <<<< prints " right Now?" without errors about trying to
//access private members
Properties are declared at the start of the class like:
public $property1;
private $property2;
protected $property3;
Then,
1. all properties can be accessed from any method of the same class like $this->property.
2. $property1 can be accessed from all methods in ANY class.
3. $property2 can be accessed from all methods in the SAME class.
4. $property3 can be accessed from all methods in the SAME class and classes which EXTEND THE SAME class.
E.g. You can access a private property from a public method:
class A {
private $property;
public function getProperty() {
return $this->property
}
Then, in a controller, you could do:
$obj = new A;
$property = $obj->getProperty();
but not:
$obj = new A;
$property = $obj->property;
Sure, you can:
class MySampleClass {
/**
* #var string
*/
private $aStringVariable;
/**
* #param string $anIP
*/
protected function __construct( $anIP ) {
$this->aStringVariable = $anIP;
}
}
// How to use the class
$myClassObject = new MySampleClass( '192.168.0.1' );
You might wish to download the eval-version of PHPStorm, a PHP IDE. The IDE might help, since it points out errors while you type and PHPStorm provides auto-completion.
Additionally, have a look in the PHP Wiki of StackOverflow. The Wiki provides valuable information.
Here is a simple example of using property.
class HelloProperty{
private $mySimpleProperty;
public function setSimpleProperty($mySimpleProperty){
$this->mySimpleProperty = $mySimpleProperty;
}
public function getSimpleProperty(){
return $this->mySimpleProperty;
}
}
$obj = new HelloProperty();
$obj->setSimpleProperty('Hello Property');
echo $obj->getSimpleProperty();
:)
Related
class KD_DB extends PDO {
protected static $dbOne = '';
protected static $dbTwo = '';
public function setVariable ($alias,$content){
switch($alias){
case'one': self::$dbOne = $content; break;
case'two': self::$dbTwo = $content; break;
}
}
}
Is there a way to create these dynamically?
Something like this to create the protected static variables
public function setVariable ($alias,$content){
self::${$alias} = $content;
}
It did not work, but I suspect it is because I need it to be static to make it to work with a third class that extends this one...
If you only have the two variables, it may be easier (with more appropriate names) to set them using a static function for each one, something like...
class KD_DB {
public static $dbOne = '';
public static $dbTwo = '';
public static function setOne ($content){
self::$dbOne = $content;
}
}
KD_DB::setOne("value for one");
var_dump(KD_DB::$dbOne);
(code with minor changes to show the process)
But if you wanted a more open ended method, I would go for an associative array as the static variables and then use the 1 method (like you currently are) to set the value in the array...
class KD_DB {
public static $data = [];
public static function setVariable ($alias,$content){
self::$data[$alias] = $content;
}
}
KD_DB::setVariable("three", "value for three");
var_dump(KD_DB::$data);
this method can have issues if you mistype a variable reference which should be found during testing though, but does offer flexibility.
I have an abstract class like this :
<?php
abstract class NoCie {
const SC = 01;
const MTL = 02;
const LAV = 03;
}
?>
I would like to test if a variable $x contain value from this abstract class only.
For now i used $x instanceof NoCie but this is not working probably because this class is abstract and can't be instantiated.
Here is the code that i'm trying to use to validate.
class CustomersTaxes
{
public $NoCie;
private $_file;
public function __construct($file)
{
$this->_file = $file;
}
public function CheckValidAndWrite()
{
$error = false;
//Numéro de compagnie
if (!($this->NoCie instanceof NoCie)) {
$error = true;
}
}
}
Here is my code that instantiate this class :
$t = new CustomersTaxes($_SERVER['DOCUMENT_ROOT'] . '/test.xlsx');
$t->NoCie = NoCie::SC;
$t->NoClient = "d";
$t->CheckValidAndWrite();
How can i do that?
I think you are confusing two concepts, but maybe what you want can be achieved in some other way. The only thing I can think of right now is to use PHP method type-hinting. But I would refactor slightly, making the NoCie property protected to be manipulated only by a getter and a setter. Like this:
class CustomersTaxes
{
private $NoCie;
private $_file;
public function __construct($file)
{
$this->_file = $file;
}
public function getNoCie()
{
return $this->NoCie;
}
public function setNoCie(NoCie $NoCie)
{
$this->NoCie = $NoCie::VALUE;
}
}
You still need a class that extends the abstract one, though, otherwise it'll never work:
class SCA extends NoCie
{
const VALUE = '01';
}
Since the NoCie property on CustomersTaxes is private, you have to set it a bit differently:
$t = new CustomersTaxes($_SERVER['DOCUMENT_ROOT'] . '/test.xlsx');
$t->setNoCie(new SCA());
// ...
That way you can make sure that whenever a NoCie property is set, it will be the class you want. No need to validate -- if setNoCie is triggered by an invalid value, it'll throw an exception.
I figured out another way to do this job without type hinting. Type hinting seem to be a good way but need to much files to work with psr-4 autoloader.
My choice is to use ReflectionClass to get all constant as array and compare value from $this->SC.
$NoCieReflection = new \ReflectionClass('\\Ogasys\\Enum\\NoCie');
if (!in_array($this->NoCie, $NoCieReflection->getConstants())) {
$error = true;
array_push($msg, "# de compagnie invalide");
}
I am getting this error and i can't see what i am doing wrong. I have done the same thing with other objects from other classes which are built in the exact same way and i can't see why i am getting this error now.
The code in which i create the object is this one:
$consulta2 = "SELECT * FROM TiposDireccion WHERE Cliente_CIF='$cif' and Direccion_Direccion='$direccion' and Direccion_CP=$cp ";
echo($consulta2."</br>");
if ($resultado2 = $conexion->query($consulta2)){
while($fila2 = $resultado2->fetch_object()){
$tipodireccion78=$fila2->TipoDireccion_Tipo;
//we see here that the select is returning a correct string with a correct value
echo($tipodireccion78);
//we try to instantiate and it fails =(
$unTipoDireccion=TipoDireccion::constructor1($tipodireccion78);
This is the class TipoDireccion:
<?php
class TipoDireccion{
private $tipo;
private $descripcion;
//Construct auxiliar
function __construct() {
}
//Constructor 1 : completo
function constructor1($tipo) {
$tipoDireccion = new TipoDireccion();
$tipoDireccion->tipo = $tipo;
return $tipoDireccion;
}
function ponTipo($tipo) {
$this->tipo = $tipo;
}
function devuelveTipo() {
return $this->tipo;
}
function ponDescripcion($descripcion) {
$this->descripcion = $descripcion;
}
function devuelveDescripcion() {
return $this->descripcion;
}
}
?>
Thank you a lot in advance!
Don't know if this is still relevant to you, but in case anyone else comes on here for an answer. The problem is in this function:
function constructor1($tipo) {
$tipoDireccion = new TipoDireccion();
$tipoDireccion->tipo = $tipo;
return $tipoDireccion;
}
Because in the class definition, you define private $tipo; and then you try and assign $tipoDireccion->tipo to what was passed through the function. However, you aren't trying to access that variable through the scope of the class, you are trying to assign it from the 'public' scope as far as the class is concerned.
The fix for this has two options, the first one would be to change private $tipo; to public $tipo;. But that isn't a good solution as you have an assignment function for it.
Instead, use your functions that you made, which would make the function look like:
function constructor1($tipo) {
$tipoDireccion = new TipoDireccion();
$tipoDireccion->ponTipo($tipo);
return $tipoDireccion;
}
That's how you need to access it from the public scope, which you are doing after you initiate a new one.
function constructor1($tipo) {}
should be
static function constructor1($tipo) {}
I have a reoccuring problem that I am currently tackling like so -
a POST variable coming in to the script which has a platform, the platform is from a list such as: xbox,ps3,pc,mobileapp,mobilegame etc
for each different platform I want to be able to do something different in my script but in some cases I want code to do very similar things at the moment I do something like this:
$platformArray = array(
'ps3'=>array('displayName'=>'playstation 3','function'=>'funcPS3'),
'xbox'=>array('displayName'=>'Xbox','function'=>'funcXbox')
)
//similar amongst all platforms code on line below
echo 'you have a :'.$platformArray[$_POST['platform']]['displayName'].' for playing games';
call_user_func($platformArray[$_POST['platform']['function']);
function funcPS3(){
echo 'ps3 specific code';
}
function funcXbox(){
echo 'xbox specific code';
}
I want to move towards a OOP approach in my code, I want to use objects as my data storage medium rather than arrays as I'm doing now, but I do sometimes need to define attributes in the code ahead of time, how could I do the above but with objects?
I would recommend for you to start by understanding polymorphism. This lecture should be good start.
When you are trying to create behavior, based on some flag, you should implement two classes with same interface:
class Xbox
{
private $displayName = 'XBox 360';
public function identify()
{
// Xbox-specific stuff
return ':::::::::::'. $this->displayName;
}
}
class PS3
{
private $displayName = 'Playstation 3';
public function identify()
{
// playstation-specific stuff
return '+++'. $this->displayName . '+++';
}
}
The two classes have method with same name that would do different things;
$platform = $_POST['platform'];
// classes in PHP are case-insensitive
// expected values would be: xbox, Xbox, ps3, pS3
if ( !class_exists($platform) )
{
echo "Platform '{$platform}' is not supported";
exit;
// since continuing at this point would cause a fatal error,
// better to simply exit
}
$object = new $platform;
echo $object->identify();
Basically, in this case you really do not care, which type of platform you are working with. All you need to know is that they both have same public interface. This is called "polymorphic behavior".
I'm going to work from a very naive OO version, to what is considered "good" OO code, using polymorphic behavior and avoiding global state.
1. Not polymorphic and has global static data
This is pretty bad because it is really just a wrapper object over procedural code. It needs a map of functions to call for each type of platform.
class Platform {
private static $platformArray = array(
'ps3' => array(
'displayName'=>'playstation 3',
'function'=>'funcPS3'
),
'xbox' => array(
'displayName'=>'Xbox',
'function'=>'funcXbox'
)
);
private $type;
public function __construct($type) {
if (!array_key_exists($type, self::$platformArray)) {
throw new Exception("Invalid Platform type $type" );
}
$this->type = $type;
}
public function printCode() {
// This was a question embedded within your question, you can use
// http://php.net/manual/en/function.call-user-func.php
// and pass an instance with a method name.
return call_user_func( array($this, self::$platformArray[$this->type]) );
}
private function funcPS3(){
echo 'ps3 specific code';
}
private function funcXbox(){
echo 'xbox specific code';
}
}
$plat = new Platform($_POST['platform']);
$plat->printCode();
2. Polymorphic... but it still uses global data
By creating a base class you can implement behavior in subclasses, creating separate class for each concern. The big problem here is that subclasses need to register with a global registry.
abstract class Platform {
abstract protected function getCode();
public function printCode() {
echo $this->getCode();
}
private function __construct() {} // so only factory can instantiate it
private static $platformArray = array();
public static function create($type) {
if (!array_key_exists($type, self::$platformArray)) {
throw new Exception("Invalid Platform type $type" );
}
return new self::$platformArray[$type];
}
public static function addPlatform($type, $ctor) {
if (!is_subclass_of($ctor, 'Platform')) {
throw new Exception("Invalid Constructor for Platform $ctor" );
}
self::$platformArray[$type] = $ctor;
}
}
class PlatformXBox extends Platform{
protected function getCode() {
return 'xbox specific code';
}
}
Platform::addPlatform('xbox', 'PlatformXBox');
class PlatformPs3 extends Platform {
protected function getCode() {
return 'ps3 specific code';
}
}
Platform::addPlatform('ps3', 'PlatformPs3');
$plat = Platform::create($_POST['platform']);
$plat->printCode();
3. Polymorphic, no global data
By putting your code into a namespace, you avoid the static code in the base class and avoid the dangers of mapping post parameters directly into classes.
namespace platform {
interface IPlatform {
public function getDisplayName();
public function getCode();
}
class PlatformFactory {
static public function create($platformType) {
$className = "\\platform\\$platformType";
if ( !is_subclass_of($className, "\\platform\\IPlatform") ){
return null;
}
return new $className;
}
}
class Xbox implements IPlatform {
public function getDisplayName(){
return 'xbox';
}
public function getCode(){
return 'xbox code';
}
}
class Ps3 implements IPlatform {
public function getDisplayName(){
return 'ps3';
}
public function getCode(){
return 'ps3 code';
}
}
}
Now you can use those classes like the following
$platform = platform\PlatformFactory::create('xbox');
echo $platform->getCode() ."\n" ;
$platform2 = platform\PlatformFactory::create('ps3');
echo $platform2->getDisplayName()."\n";
$noPlatform = platform\PlatformFactory::create('dontexist');
if ($noPlatform) {
echo "This is bad, plaftorm 'dontexist' shouldn't have been created";
} else {
echo "Platform 'dontexist' doesn't exist";
}
You might want to create a class called platforms and within the class a different method for each platform:
class platforms {
//Create your variables here, also called properties.
public $displayName;
//Create a function, also called a method for each platform you intent to use.
public function xboxPlatform(){
//Code comes here what you want to do.
}
}
Hope this helps.
This question already has answers here:
PHP Readonly Properties?
(7 answers)
Closed last year.
When trying to change it,throw an exception.
I suppose a solution, for class properties, would be to :
not define a property with the name that interests you
use the magic __get method to access that property, using the "fake" name
define the __set method so it throws an exception when trying to set that property.
See Overloading, for more informations on magic methods.
For variables, I don't think it's possible to have a read-only variable for which PHP will throw an exception when you're trying to write to it.
For instance, consider this little class :
class MyClass {
protected $_data = array(
'myVar' => 'test'
);
public function __get($name) {
if (isset($this->_data[$name])) {
return $this->_data[$name];
} else {
// non-existant property
// => up to you to decide what to do
}
}
public function __set($name, $value) {
if ($name === 'myVar') {
throw new Exception("not allowed : $name");
} else {
// => up to you to decide what to do
}
}
}
Instanciating the class and trying to read the property :
$a = new MyClass();
echo $a->myVar . '<br />';
Will get you the expected output :
test
While trying to write to the property :
$a->myVar = 10;
Will get you an Exception :
Exception: not allowed : myVar in /.../temp.php on line 19
class test {
const CANT_CHANGE_ME = 1;
}
and you refer it as test::CANT_CHANGE_ME
Use a constant. Keyword const
The short answer is you can't create a read-only object member variable in PHP.
In fact, most object-oriented languages consider it poor form to expose member variables publicly anyway... (C# being the big, ugly exception with its property-constructs).
If you want a class variable, use the const keyword:
class MyClass {
public const myVariable = 'x';
}
This variable can be accessed:
echo MyClass::myVariable;
This variable will exist in exactly one version regardless of how many different objects of type MyClass you create, and in most object-oriented scenarios it has little to no use.
If, however, you want a read-only variable that can have different values per object, you should use a private member variable and an accessor method (a k a getter):
class MyClass {
private $myVariable;
public function getMyVariable() {
return $this->myVariable;
}
public function __construct($myVar) {
$this->myVariable = $myVar;
}
}
The variable is set in the constructor, and it's being made read-only by not having a setter. But each instance of MyClass can have its own value for myVariable.
$a = new MyClass(1);
$b = new MyClass(2);
echo $a->getMyVariable(); // 1
echo $b->getMyVariable(); // 2
$a->setMyVariable(3); // causes an error - the method doesn't exist
$a->myVariable = 3; // also error - the variable is private
I made another version that uses #readonly in the docblock instead of private $r_propname. This still doesn't stop the declaring class from setting the property, but will work for public readonly access.
Sample Class:
class Person {
use Readonly;
/**
* #readonly
*/
protected $name;
protected $phoneNumber;
public function __construct($name){
$this->name = $name;
$this->phoneNumber = '123-555-1234';
}
}
The ReadOnly trait
trait Readonly {
public function readonly_getProperty($prop){
if (!property_exists($this,$prop)){
//pretty close to the standard error if a protected property is accessed from a public scope
trigger_error('Undefined property: '.get_class($this).'::\$'.$prop,E_USER_NOTICE);
}
$refProp = new \ReflectionProperty($this, $prop);
$docblock = $refProp->getDocComment();
// a * followed by any number of spaces, followed by #readonly
$allow_read = preg_match('/\*\s*\#readonly/', $docblock);
if ($allow_read){
$actual = $this->$prop;
return $actual;
}
throw new \Error("Cannot access non-public property '{$prop}' of class '".get_class($this)."'");
}
public function __get($prop){
return $this->readonly_getProperty($prop);
}
}
See the source code & test on my gitlab
I cooked up a version, too, using a trait.
Though in this case, the property can still be set by its declaring class.
Declare a class like:
class Person {
use Readonly;
protected $name;
//simply declaring this means "the 'name' property can be read by anyone"
private $r_name;
}
And this is the trait I made:
trait Readonly {
public function readonly_getProperty($prop){
if (!property_exists($this,$prop)){
//pretty close to the standard error if a protected property is accessed from a public scope
// throw new \Error("Property '{$prop}' on class '".get_class($this)."' does not exist");
trigger_error('Undefined property: '.get_class($this).'::\$'.$prop,E_USER_NOTICE);
}
$allow_read = property_exists($this, 'r_'.$prop );
if ($allow_read){
$actual = $this->$prop;
return $actual;
}
throw new \Error("Cannot access non-public property '{$prop}' of class '".get_class($this)."'");
}
public function __get($prop){
return $this->readonly_getProperty($prop);
}
}
See the source code & test on my gitlab
I know this is an old question, but PASCAL's answer really helped me and I wanted to add to it a bit.
__get() fires not only on nonexistent properties, but "inaccessible" ones as well, e.g. protected ones. This makes it easy to make read-only properties!
class MyClass {
protected $this;
protected $that;
protected $theOther;
public function __get( $name ) {
if ( isset( $this->$name ) ) {
return $this->$name;
} else {
throw new Exception( "Call to nonexistent '$name' property of MyClass class" );
return false;
}
}
public function __set( $name ) {
if ( isset( $this->$name ) ) {
throw new Exception( "Tried to set nonexistent '$name' property of MyClass class" );
return false;
} else {
throw new Exception( "Tried to set read-only '$name' property of MyClass class" );
return false;
}
}
}