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");
}
Related
I am trying to write a unit test for a function that immediately loads an object from a different class that uses the input to the function as a parameter. I am new to php unit testing and couldn't find anything that address my particular problem. A few leads that I had that led to no avail was using an injector, and trying to us a reflection.
The code I am trying to write a unit test for is:
public static function isUseful($item) {
$objPromo = MyPromoCodes::Load($item->SavedSku);
if (!is_null($objPromo)
&& ($objPromo->PromoType == MyPromoCodes::Interesting_Promo_Type)) {
return true;
}
return false;
}
My attempt at mocking this out:
public function testIsUseful() {
$injector = $this->getMockBuilder('MyPromoCodes')
->setMethods(array('Load'))
->getMock();
$objPromo = $this->getMock('MyPromoCodes');
$objPromo->PromoType = 'very interesting promo type';
$injector->set($objPromo, 'MyPromoCodes');
$lineItem1 = $this->getDBMock('LineItem');
$this->assertTrue(MyClass::isUseful($lineItem1));
}
however this doesn't work because there is no set method for this object....
Not sure what else to try, any help would be appreciated.
I made the library that makes static classes mocking possible:
class MyClass {
public static $myPromoCodes = 'myPromoCodes';
public static function isUseful($item) {
$objPromo = self::$MyPromoCodes::Load($item->SavedSku);
if (!is_null($objPromo)
&& ($objPromo->PromoType == MyPromoCodes::Interesting_Promo_Type)) {
return true;
}
return false;
}
}
class MyClassTest extends \PHPUnit_Framework_TestCase
{
public function testSomething()
{
$myClass = Moka::stubClass('MyClass');
$myClass::$myPromoCodes = Moka::stubClass(null, ['::Load' => (object)[
'PromoType' => MyPromoCodes::Interesting_Promo_Type
]]);
$this->assertTrue($myClass::isUseful((object)['SavedSku' => 'SKU']);
$this->assertEquals([['SKU']], $myClass::$myPromoCodes->moka->report('::Load'));
}
}
To start with you cannot mock static method with PHPUnit. At least not with 4.x and 5.x.
I would suggest a DI approach like this:
class MyClass
{
private $promoCodesRepository;
public function __construct(MyPromoCodesRepository $promoCodesRepository)
{
$this->promoCodesRepository = $promoCodesRepository;
}
public function isUseful(MyItem $item)
{
$objPromo = $this->promoCodesRepository->Load($item->SavedSku);
// ...
}
}
Here you can easily mock the Load method.
Unfortunately the "static" approach creates a lot of issues during tests so it is better to avoid it whenever possible.
I am trying to add functions to class from a separate file, I wonder if this could be possible!
$mClass = new MyClass();
$mClass->new_Functions[0](10); // Is there a way to have it in this form?
class myClass
{
private $Pvar = 5;
$new_Fcuntions;
function __construct()
{
include('additional.functions.php');
$arr = get_defined_functions();
$this->new_Functions = $arr['user'];
// trying to call the function with parameter 10
call_user_func(array($this, $this->new_Functions[0]), 10);
}
}
[additional.functions.php] file
function operate($y)
{
return $this->Pvar * $y;
}
----- Edited ------- as it wasn't clear!
"additional.functions.php" is a module and there will be multiple modules to be added to the application, and every module could have more than single function and modules could call one another!
additional.functions.php [module file]
function operate($y)
{
return $this->Pvar * $y;
}
function do-more($foo)
{
return $this->operate(20) + $foo;
}
another.functions.php [another module]
function do-another($foo)
{
return $this->do-more(30) - $foo;
}
function add($foo, $bar)
{
return $foo + $bar;
}
appreciate every participation, its been a while since I am trying to maneuver around with it!
Is this possible or should I give up!
It looks to me like you are looking for Traits, which are a new feature as of PHP 5.4.0. Using traits, you can have snippets of code "mixed in" to other classes, a concept known as "horizontal reuse".
If you are not looking for traits, it's possible that you could do what you wanted with Runkit, however I would suggest staying as far away from it as possible, if you are not genuinely interested in PHP internals as well.
In any event, whatever you are trying to do is very interesting
I got it to work with dependency injection. The pvar has to be public or create a __get method to return the private variable. I also used the function name because it seems cleaner to me to use it via name rather than it's position in the list but if you want to keep that then just put $key where you see $value from the line: $this->function_list[$value] = ...
function operate($y, $that)
{
return $that->Pvar * $y;
}
class Example {
public $function_list = array();
private $Pvar = 5;
public function __construct()
{
$list = get_defined_functions();
$that = $this;
foreach ($list['user'] as $key => $value) {
$this->function_list[$value] = function() use ($value, $that) {
print call_user_func_array($value, array_merge(func_get_args(), array($that )));
};
}
}
public function __get($key)
{
if (isSet($this->$key)) {
return $this->$key;
} else {
throw new \Exception('Key "'.$key.'" does not exist');
}
}
}
$Ex = new Example();
$Ex->function_list['operate'](10);
If you want to extend MyClass from your modules (and not to initialize it, like in your example code), than you could do it in a way like this:
<?php
namespace modules\MyModuleA;
class MyClassExtension
{
private $MyObject;
public function __construct(\MyClass $MyObject)
{
$this->MyObject = $MyObject;
}
public function doSomething($anyParameter)
{
return $this->MyObject->doSomethingElse($anyParameter * 5, 42, 'foo');
}
}
And MyClass:
<?php
class MyClass extends \Extensible
{
// some code
}
abstract class Extensible
{
private $extensions = [];
public function extend($extension)
{
$this->extensions[] = $extension;
}
public function __call($methodName, $parameters)
{
foreach ($this->extensions as $Extension) {
if (in_array($methodName, get_class_methods($Extension))
return call_user_func_array([$Extension, $methodName], $parameters);
}
throw new \Exception('Call to undefined method ' . $methodName . '...');
}
public function hasExtension($extensionName)
{
return in_array($this->extensions, $extensionName);
}
}
And put it all together:
<?php
$moduleNames = ['MyModuleA', 'MyModuleB'];
$MyObject = new \MyClass;
foreach ($moduleNames as $moduleName) {
$className = '\\modules\\' . $moduleName . '\\MyClassExtension';
$module = new $className($MyObject);
$MyObject->extend($module);
}
// Now you can call a method, that has been added by MyModuleA:
$MyObject->doSomething(10);
You should add an interface for the extension classes of course...
The problem is: What happens if any code in your application calls a method of $MyObject, that is not there, because the module has not been loaded. You would always have to check if ($MyObject->hasExtension('ModuleA')) { ... }, but, of course, the application shouldn't be aware of any module. So I would not design an application in such a way.
I would suggest to use traits (mix-ins). See PHP reference
If you can have another class in that file instead of file with functions
- the best solution will be Traits
http://php.net/manual/en/language.oop5.traits.php
or using inheritance
If you move that code to class you can avoid a lot of unnecessary code. I mean:
include('additional.functions.php');
$arr = get_defined_functions();
$this->new_Functions = $arr['user'];
// trying to call the function with parameter 10
call_user_func(array($this, $this->new_Functions[0]), 10);
It'll be e.g.:
class myClass extends MyBaseClassWithMyAwesomeFunctions
{
private $Pvar = 5;
}
Maybe this approach helps you:
In the files with the additional functions, don't define named functions, but return a closure, that expects (at least) the object (instance of MyClass) as parameter:
<?php
// additional.functions.php
return function ($myObject) {
$Object->multiplyPvar($myObject->getTheNumber());
$Object->doSomethingElse(42, 'foo');
};
The client, that builds MyClass collects those functions from the files into the array:
<?php
$files = [
'/path/to/my/additional.functions1.php',
'/path/to/my/additional.functions2.php'
];
$initFunctions = [];
foreach ($files as $path)
$initFunctions[] = include $path;
$MyObject = new \MyClass($initFunctions);
The constructor then calls those functions:
<?php
class MyClass
{
public function __construct(array $additionalInitFunctions)
{
foreach ($additionalInitFunctions as $additionalInitFunction)
$additionalInitializerFunction($this); // you can also add parameters of course
}
}
This way the class keeps very well testable as well as the function files. Maybe this could help you in any way. You should never ever think about modifying the internal (private) state of an object directly from any code from outside of the class. This is not testable! Think about writing tests before you implement your code (called "test driven development"). You will see, it is not possible to test a class, if you allow any code outside of that class to modify the internal (private) state of the class instance. And you don't want to have this. If you change some internal implementation detail in your class without breaking the unit test of that class, you will anyways probably break some code in any of your additional.functions.php files and no test will tell you: "Hey: you've broken something right now".
I'm trying to write some abstract MVC classes in PHP using PHPStorm. Some of the overriding class properties I am using for generic class wrappers tend to be better suited to using static methods and properties.
The code works fine, but whenever I use a variable to represent a class name when pointing it at a static variable (one that exists in inherited methods) PHPStorm doesn't know how to handle it and shows it as an error in the IDE. I'm just wondering if there's a better way to do this sort of thing or if it's simply going to be something I have to learn to ignore in the IDE.
Example:
class AbstractModel {
protected static $_dataMapper = null;
protected static $_subs = null;
public function __construct($data=null) {
// constructor omitted with things like a generic class init, etc.
}
// $_subs is a static array who's value is a class name use for
// populating sub-objects as part of a class instance
public function setSubProperty($subKeyName,$value) {
$dataType = get_called_class();
/*************************************
* PHPStorm complains here on $_subs *
*************************************/
if(is_array($dataType::$_subs)
&& array_key_exists($subKeyName,$dataType::$_subs)) {
$setMethod = 'set' . ucfirst($subKeyName);
return $dataType->$setMethod($value);
} else {
return false; // or throw an exception
}
}
}
class SomedataModel extends AbstractModel {
public $other = null;
protected static $_subs = array(
'other' => "OtherModel"
);
public function __construct($data=null) {
parent::__construct($data=null);
foreach(array_keys(self::$_subs) as $_k) {
$setMethod = 'set'.ucfirst($_k);
$this->$setMethod();
}
}
public function setOther($data=null) {
// sanitize and set instance of 'other' as $this->other
}
}
You can easily work around that by using the static keyword instead:
...
public function setSubProperty($subKeyName,$value) {
if (is_array(static::$_subs)
&& array_key_exists($subKeyName, static::$_subs)) {
$setMethod = 'set' . ucfirst($subKeyName);
...
PHPStorm supports it very well. Support of variable string values as classnames resolution of static members and properties is not supported. You might want to open a feature request (if it does not yet exists), however I doubt it's technically feasible because it wouldn't be type-hinting but value-hinting which I think is not supported in Phpstorm.
I have the following code:
<?php
class X
{
public function do($url)
{
$httpRequest = new \HttpRequest\Curl($url, $this->getOptions());
$httpRequest->fire();
// etc.
}
// ...
}
In order to be able to unit test this class, I'd like to inject a mocked HttpRequest class. One way to do this would be as follows:
<?php
class X
{
private $httpRequestClass;
public function __construct($httpRequestClass = '\HttpRequest\Curl')
{
$this->httpRequestClass = $httpRequestClass;
}
public function do($url)
{
$httpRequest = new $this->httpRequestClass($url, $this->getOptions());
$httpRequest->fire();
// etc.
}
// ...
}
But this doesn't seem right. Any other ideas?
public function __construct($url, $httpRequestClass = null)
{
$this->url = $url;
if ($httpRequestClass == null) //> Default
$this->httpRequestClass = new HttpRequest\Curl($this->url);
else
$this->httpRequestClass = $httpRequestClass;
}
so when you are using this class normally just call it with one param
yourClass('your url');
Otherwise pass the istance in the second argument
yourClass('url', new MockedObj);
Of course you should always Inject your dependencies without providing a default object
The class needs to generate objects of type HttpRequest, but we don't necessarily want it to initialize an object: we may want it to use the prototype pattern, for example. Therefore, the class calls for the factory pattern. I chose a factory callback, as opposed to a factory class, for brevity.
<?php
class X
{
private $factoryCallback;
public function __construct($factoryCallback = null)
{
$this->factoryCallback = $factoryCallback;
}
public function do($url)
{
$httpRequest = $this->createHttpRequest($url);
$httpRequest->fire();
// etc.
}
private function createHttpRequest($url)
{
$callback = $this->factoryCallback;
if (is_callable($callback)) {
return $callback($url, $this->getOptions());
}
return new \HttpRequest\Curl($url, $this->getOptions());
}
// ...
}
The helper method, createHttpRequest(), is a bit redundant in this example, but would be used for error handling in production code.
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;
}
}
}