I am rather new to the whole OOP paradigm in PHP, but I'm really loving it so far. I am currently writing a EventSender class, which should gather some information, and then fire the event to a EventHandler as well as writing the event to a eventlog.
When I came to the "firing" part, it struck me that I really would love a simple solution to validating that all my declared variables had been set. Is there an easy way to do so, or maybe even a in-built function in PHP?
Also, the code pasted below is the actual code for my class so far, so if you have any other remarks feel free to elaborate your thought :-)
class Event extends Base {
private $eventKey;
private $userID;
private $value;
private function __construct($eventKey){
$sql = Dbc::getInstance();
//Set and escape the EVENT_KEY.
$this->eventKey = $sql->real_escape_string($eventKey);
$sql->select_db('my_event_db');
$result = $sql->query('SELECT idx FROM event_types WHERE event_key = $this->$eventKey');
//Verify that the event key given is correct and usable.
//If failed throw exception and die.
if($result->num_rows != 1){
$err = 'ERROR: Illegal EVENT_KEY sent.';
throw new Exception($err);
}
}
public function setUserID($userID) {
$this->userID = $userID;
if(is_numeric($this->userID) != TRUE){
$err = 'ERROR: Value passed as userID is not numeric.';
throw new Exception($err);
}
}
public function setValue($value) {
$this->value = $value;
//The isJson function comes from a Trait that my Base class uses.
//The method will also throw a exception if it doesn't pass the test.
self::isJson($this->value);
}
public function fire () {
/* Here I want some code that swiftly checks if all declared vars have been set, which makes my method "ready to fire".. */
}
Best regards,
André V.
Based on Lajos Veres answer, I managed to build a class which can be added to a Trait (what is what I did in this case) and did exactly what I wrote in my initial question. I just wanted to share it, if anyone wanted to reuse it :-)
protected function allPropertiesSet($self){
/*
* This class is dependent on the ReflectionClass.
* This class can be called with self::allPropertiesSet(get_class());
* The class can be called in any class who uses this trait, even if it is inherited. It's function is to validate if all your defined variables are set.
* It will return true if all variables are set, otherwise it will return false.
* Some credit goes to Lajos Veres from Stackoverflow for pointing me in the right direction.
* Author: André Valentin
* Created: 30-10-2013
*/
$class = new $self;
$reflect = new ReflectionClass($class);
$props = $reflect->getProperties(ReflectionProperty::IS_PUBLIC | ReflectionProperty::IS_PROTECTED | ReflectionProperty::IS_PRIVATE | ReflectionProperty::IS_STATIC);
$prop_array = array();
foreach($props AS $prop){
$var_name = $prop->getName();
$class_name = $prop->class;
if($class_name == $self){
$prop_array[] = $var_name;
}
}
foreach($prop_array AS $value){
$var_name = $value;
if(!isset($this->$var_name)){
return false;
}
}
return true;
}
Using Reflection you can list the properties of a class
http://www.php.net/manual/en/reflectionclass.getproperties.php
But I think this is overkill...
For sanity's sake, clearly define the rules governing the state of your instance before you call fire(), and move that to a separate function. So your fire() becomes
function fire() {
if ($this->validate_state()) {
/// do whatever
...
} else {
/// report issues
}
}
Your validator simply checks everything that needs to be in-place, i.e.
function validate_state() {
if ( isset($this->some_property) )
....
}
Alternatively, if your state checking just wants to ensure that default values are set, make sure this is done in your __construct constructor. That way you know which values are reasonably expected to defined.
Related
I am trying to unit test a function which is in an entity class, and it is stored in my DB by the use of a constructor. Each time I am trying to test this function it is giving me that error
ArgumentCountError: Too few arguments to function App\Entity\Deal::__construct(), 0 passed in /var/www/html/casus/tests/dealsEntityFunctionsTest.php on line 10 and exactly 1 expected
It is obvious I think, but I am really new with unit testing and that stuff so I couldn't find the answer. Could you please help me?
My code is
class Deal
{
private bool $isNewToday
public function __construct($deal)
{
$this->isNewToday = $deal['is_new_today'];
}
public function getIsNewToday(): ?bool
{
return $this->isNewToday;
}
public function setIsNewToday(bool $isNewToday): self
{
$this->isNewToday = $isNewToday;
return $this;
}
}
And my unit test is
class test extends TestCase
{
public function testIsNewTodayIsTrue()
{
$deal = new Deal();
$deal->setIsForSale(true);
$this->assertTrue($deal->getIsForSale(), true);
}
}
As brombeer suggested, new Deal entity requires parameter.
This parameter looks like an array, with key 'is_new_today'. So, sth like this below should help with constructor error.
class test extends TestCase
{
public function testIsNewTodayIsTrue()
{
$deal = new Deal(['is_new_today' => true]);
$deal->setIsForSale(true);
$this->assertTrue($deal->getIsForSale(), true);
}
}
This has nothing to do with Unit Testing, or Symfony, or any of the other details you mentioned. You've defined something with a mandatory parameter, and then aren't passing that parameter.
Just like any function, the parameters to a constructor are mandatory unless you provide a default. And if you write code that assumes the parameter will have a particular format, you need to provide a value that meets that assumption.
So either pass the parameter every time you create the object, with whatever format the constructor expects:
$deal = new Deal(['is_new_today' => false]);
... or make it optional, and decide what should happen if it's not passed:
class Deal
{
private bool $isNewToday
public function __construct(?array $deal = null)
{
if ( isset($deal) ) {
$this->isNewToday = $deal['is_new_today'];
}
else {
$this->isNewToday = false;
}
}
}
Note that $isNewToday is defined as a non-nullable boolean, so you should always give it a value in the constructor, or an inline default, like private bool $isNewToday = false; Otherwise, you'll get "uninitialized value" errors if you try to read it. For that reason, the return type of ?bool on getIsNewToday() doesn't make sense - it can't return null, because $this->isNewToday can never be bool.
I am testing a class, let's call it ClassUnderTest using another class, let's call it OtherClass. In my Test I do:
$OtherClassStub = $this->createStub(OtherClass::class);
$OtherClassStub->method(...)
->willReturn(...);
$ClassUnderTest->otherClass = $OtherClassStub;
That works. But when the $ClassUnderTest calls new OtherClass(), the original OtherClass class is created instead of the stub.
How can I achieve that every possible instance of OtherClass in the context of the test is replaced by the stub?
From your description I infer that in principle you have something like this:
class OtherClass {
protected function someMethod(): bool
{
// determine $x ...
return $x;
}
}
class ClassUnderTest {
public OtherClass $otherClass;
public function methodToBeTested(): bool
{
$otherClass = new OtherClass();
return $otherClass->someMethod();
}
}
class ClassUnderTestTest extends TestCase {
public function testMethodToBeTested(): void
{
$otherClassStub = $this->createStub(OtherClass::class);
$otherClassStub->method('someMethod')
->willReturn(true);
$classUnderTest = new ClassUnderTest();
$classUnderTest->otherClass = $otherClassStub;
$result = $classUnderTest->methodToBeTested();
$this->assertTrue($result);
}
}
Now the assertion in your test may hold or it may fail. Why? Because you are not calling the method you stubbed on the $otherClassStub. Instead you instantiate a new $otherClass object in the method you're testing (or somewhere down the line).
Either your ClassUnderTest should always use the OtherClass object from the ClassUndertTest::otherClass attribute (assuming that's why you put it there in the first place).
Or you could use some other form of dependency injection, e.g. by using a framework like Symfony or Laravel. (In the case of Symfony you can even use only the DependencyInjection Component, no idea if that's possible with Laravel, too.)
The simple answer to your actual question is: you cannot change the behaviour of the new keyword. Calling new on a class will always instantiate a new object based on exactly that class, unless the constructor of that class defines something else.
(You might want to get the concept of classes and objects straight, your code example as well as your question seem to indicate that you're not quite clear on that. Maybe reading up on that as well as on the concept of dependency injection will help you.)
Perhaps a solution to your problem is presented here:
How to Build a PHP Plugin Module System
This is one way to load classes as plugins and they can be called from each other. With modifying this system a bit, you can create as many "new OtherClass()" as you like from your code and still access everything from other classes. If you want multiple instances of a class, perhaps modify it into this direction:
function load ($module,$instance) {
if (isset($this->$module->$instance)) { return true; }
From above link:
<?php
class Core {
// (A) PROPERTIES
public $error = ""; // LAST ERROR MESSAGE
public $pdo = null; // DATABASE CONNECTION
public $stmt = null; // SQL STATEMENT
public $lastID = null; // LAST INSERT/UPDATE ID
// (B) LOAD SPECIFIED MODULE
// $module : module to load
function load ($module) {
// (B1) CHECK IF MODULE IS ALREADY LOADED
if (isset($this->$module)) { return true; }
// (B2) EXTEND MODULE ON CORE OBJECT
$file = PATH_LIB . "LIB-$module.php";
if (file_exists($file)) {
require $file;
$this->$module = new $module();
// EVIL POINTER - ALLOW OBJECTS TO ACCESS EACH OTHER
$this->$module->core =& $this;
$this->$module->error =& $this->error;
$this->$module->pdo =& $this->pdo;
$this->$module->stmt =& $this->stmt;
return true;
} else {
$this->error = "$file not found!";
return false;
}
}
}
ps. thank you for the mod, who made me work a bit more to keep this answer online. the answer is so much better now.
I need to create approx. 5-7 classes, every class will contain a lot of members (let us say each class will contain 20 members). I could create them using public access, like:
class A {
public $myPropertyOne = '';
public $myPropertyTwo = '';
...
}
My preferred way of course to make these members private and create get/set methods for each property. I.e.
class A {
private $myPropertyOne = '';
private $myPropertyTwo = '';
public function getMyPropertyOne() {
return $this->myPropertyOne;
}
public function setMyPropertyOne($myPropertyOne) {
$this->myPropertyOne = $myPropertyOne;
}
public function getMyPropertyTwo() {
return $this->myPropertyTwo;
}
public function setMyPropertyTwo($myPropertyTwo) {
$this->myPropertyTwo = $myPropertyTwo;
}
}
But considering a class will have 20 properties, I will have in addition to this add 40 methods. And my concern here is how will this slow down the script and much more memory this will require (remember I am going to have several classes like this).
Another solution could be to use magic functions __set, __get but I don't want to, because the code completion in development IDE will not suggest properties which is crucial for me.
If this would be a compiled language (like C++) I would not have a question and would use the solution with getters, setters but since the PHP is interpreted language I am interested in my scripts to use less RAM and be as fast as possible.
Thanks in advance, any thoughts regarding this question would be much appreciated!
My Opinion
Thank you all for your answers, I just wanted to share my opinion in case someone will look for an answer to this question.
I cannot fully agree with those who say that you should not care about performance as this is task of optimizers, I think this is important factor (well atleast as for me), when we're dealing with interpreted language such as PHP we will always have to think about memory and speed (this all reminds me the time when I was developing system apps for DOS, heh :) and you always have been limited with poor CPU and kilobytes of total RAM so you got happy if you could save an additional byte), in PHP development you have the same picture as regardless of how many server you add, users' count will be always higher so that you always have to decide if you want to follow classic/safe/proper method or to avoid this and get some gain in speed or memory.
So.... my opinion is that the best way here is to use public access for all member and avoid getters/setters for all properties and use private access with get/set methods for properties which requires data validation or initialization before a value will be set.
For example:
class B {
public $myPropertyOne = '';
public $myPropertyTwo = '';
private $myPropertyThree = array();
public function getMyPropertyThree() {
return $this->myPropertyThree;
}
public function setMyPropertyThree($val) {
if(!is_array($val)) {
return;
}
$this->myPropertyThree = $val;
}
}
Thank you for spending time on my question!
Simple test shows instances take the same amount of memory, unaffected by the number of methods in a class:
Class with no methods:
class Test1 { }
Class with 20 methods:
class Test2 {
function test1() { return true; }
function test2() { return true; }
function test3() { return true; }
function test4() { return true; }
function test5() { return true; }
function test6() { return true; }
function test7() { return true; }
function test8() { return true; }
function test9() { return true; }
function test10() { return true; }
function test11() { return true; }
function test12() { return true; }
function test13() { return true; }
function test14() { return true; }
function test15() { return true; }
function test16() { return true; }
function test17() { return true; }
function test18() { return true; }
function test19() { return true; }
function test20() { return true; }
}
Test loop, same for both tests:
$test = array();
$base = memory_get_usage();
for ($i = 0; $i < 10000; $i++) {
$test[] = new ClassToBeTested();
}
$used = memory_get_usage() - $base;
print("used: $used\n");
Result for class Test1 (no methods):
used: 3157408
Result for class Test2 (20 methods):
used: 3157408
I've run it in two separate scripts, since running the two tests in a single script apparently exposed some PHP internal allocation, and the second test consumed less memory than the first, no matter which one is first or second.
While you surely take more memory for the actual class definition, apparently this cost is incurred only once per class, not per instance. You don't have to worry about the memory usage.
But considering a class will have 20 properties
Having this many properties is usually an indicator of misplaced information. Check whether you can group some of those into Classes of their own.
Refactoring: Extract Class
I will have in addition to this add 40 methods.
Not at all. Unless these classes are dumb data structs, you dont want any Getters and Setters on them because they break encapsulation. Put methods in the public API with which you tell the objects to do things.
Getter Eradicator
Getters and Setters are evil
Tell Don't Ask
And my concern here is how will this slow down the script and much more memory this will require (remember I am going to have several classes like this).
This is not an issue.
In PHP, are objects methods code duplicated or shared between instances?
Another solution could be to use magic functions __set, __get but I don't want to, because the code completion in development IDE will not suggest properties which is crucial for me.
Modern IDEs can autocomplete on magic methods.
Code Completion for private/protected member variables when using magic __get()
However, if you are already concerned about performance at the microlevel, then you dont want magic methods because those are definitely slower.
__get/__set/__call performance questions with PHP
Apart from that, Magic Methods are not substitutes for getters and setters but error handlers that get triggered when an inaccessible property or method was called.
PHP __get and __set magic methods
Also, magic methods are unobvious and make for hard to read APIs.
To make properties of your class that implemented by magic methods to be highlited by IDE just use #property PHPDoc #property tag, like this:
<?php
/**
* #property int id Blog post ID
* #property string title Blog post Title
*/
class Post {
}
More on PHPDoc' #property here: http://manual.phpdoc.org/HTMLSmartyConverter/PHP/phpDocumentor/tutorial_tags.property.pkg.html
As for other issues questioned - Karoly Horvath' comment fully covers those PHP OOP a lot of setters, getters.
As stated before, it's quite strange that your class should have so many properties. However, it can sometimes (fairly rarely though) happen. But normally, those properties should have a sort of link together : so you could store them in a hashmap and not a property. Then you just neeed one method as a getter.
Now, it will surely be more resources consuming, true. As for autocompletion, use constants : you'll just type something like :
$my_class->getFromHashMap($parameter)
And when typing your parameter, you'll use the constant as it's stored in the class : here, the autocomplete should be able to help you.
Take in mind that my code considered that properties' name have been declared in lowercase...
<?php
class Modelo {
var $attr1 = "default";
var $attr2 = 0;
public function __call($name, $arguments)
{
if (method_exists($this, ($method = $name))){
return $this->$method();
}
else{
$attribute = split("get",$name);
if(count($attribute)==2){
$attribute = strtolower($attribute[1]);
if(isset($this->$attribute)){
return ($this->$attribute);
}
}else{
$attribute = split("set",$name);
if(count($attribute)==2){
$attribute = strtolower($attribute[1]);
if(isset($this->$attribute) && count($arguments)==1){
$this->$attribute=$arguments[0];
}else{
die("$name number of arguments error: ".join($arguments,","));
}
}else{
die("$name doesn't exist!");
}
}
}
}
}
echo "<pre>";
$m = new Modelo();
print_r(
array(
"objetct"=>$m
,"getAttr1"=>$m->getAttr1()
,"getAttr2"=>$m->getAttr2()
)
);
echo "setAttr1\n";
$m->setAttr1("by set method");
print_r(
array(
"objetct"=>$m
,"getAttr1"=>$m->getAttr1()
,"getAttr2"=>$m->getAttr2()
)
);
?>
You could try this:
trait get_set
{
public function set($what, $value)
{
$this->{$what} = $value;
}
public function get($what)
{
return $this->{$what};
}
}
It will work on public and protected variables. You can add if(!isset($this->{$what})error()
Currently I have a class called user that I want to create with different variables, but I think I'm doing it wrong.
Currently I have a class "Unit" with these two functions
public function __construct($table, $id) {
require_once('database.php');
require_once('app.php');
require_once("postmark.php");
$this->table = $table;
$this->valid = true;
if(!$id) {
$this->valid = false;
}
$this->populate($id);
}
public function populate($id) {
$db = new DB();
$q = $db->where('id', $id)->get($this->table);
$resp = $q->fetchAll();
foreach ($resp as $row) {
foreach ($row as $key=>$value) {
if(!is_int($key))
$this->$key = html_entity_decode($value, ENT_QUOTES);
if(is_null($value)) {
$this->$key = null;
}
}
}
if(count($resp) <= 0) $this->valid = false;
$verdict = !$db->error;
$db = null;
unset($db);
return $verdict;
}
And then my "User" class extends it like so
public function __construct($id, $hash = null, $verify = null, $api = null) {
if($api)
$value = $this->apiToId($api);
else if($verify)
$value = $this->verifyToId($verify);
else if($hash)
$value = $this->hashToId($hash);
else
$value = $id;
parent::__construct("users", $value);
}
But I can't help but think this is poor in design. A few things I have seen in the past are the use of ampersands, possibly making it so I could do
$user = new User()->fromId($id);
Or
$user = new User()->withHash($hash);
Instead of passing it a long list of null params. That or I could improve the way inheritance works. While I like to think I know what I'm doing with PHP, I'd really like some help looking in the right direction. PHP's docs are so cumbersome, that I never no where to look, but always find cool useful tools. I'm wondering how I can improve this for more flexibility and structure.
Move includes to the very top of your php file. Anything that needs to be conditionally included is probably poorly designed.
Your unit class should be declared as abstract. This prevents anyone from instantiating a unit. You can only declare subclasses of it.
Any functions relating to your class should be declared as methods. Thus, the example given in an answer now-removed is a terrible choice. The function alloc really should be a static function defined in User. Code snippet at bottom.
Your init functions should be declared as static and return a new instance of the class. Defining an instance of the class to re-instantiate the class is just a bad idea.
Your database connection should use a Singleton pattern. Look it up if you need to.
Post your full code and comment on this answer if you'd like some help implementing all of this.
$user = User::initWithHash($hash);
//your create method:
/**
* Creates and returns a new instance of the class. Useful
* #return an instance of User.
*/
public static function create() {
return new User();
}
When I'm trying to serialize an object which has members including closures an exception is thrown.
To avoid the serialization of the members including closures I tried the following:
function __sleep(){
$ref = new ReflectionClass($this);
$props = $ref->getProperties();
foreach ($props as $prop){
$name = $prop->name;
if (is_callable($this->$name)===false){
$dream[] = $prop->name;
}
}
return $dream;
}
Unfortunately this does not work. Is there a better way to detect whether a property is a closure or not.
EDIT: I solved my problem by letting the closure know whether to serialize or not
To do this I am wrapping the closure itself. Here's an example:
/**
* Wrapper-class to prevent closure to be serialized.
*/
class WrappedClosure {
private $closure = NULL;
protected $reflection = NULL;
public function __construct($function){
if ( ! $function instanceOf Closure)
throw new InvalidArgumentException();
$this->closure = $function;
$this->reflection = new ReflectionFunction($function);
}
/**
* When the instance is invoked, redirect invocation to closure.
*/
public function __invoke(){
$args = func_get_args();
return $this->reflection->invokeArgs($args);
}
// do nothing on serialization
public function __sleep(){}
// do nothing on serialization
public function __wakeup(){}
}
// Assigning a wrapped closure to a member
$myObject->memberHoldingAClosure =
// Wrapping the closure
new WrappedClosure(
function (){
echo "I'am the inner closure.";
}
)
);
// the serialization doesn't throw an exception anymore
serialize($myObject);
Works fine for me:
class foo {
protected $param = 'value';
protected $closure = null;
public function __construct() {
$this->closure = function(){
return 123;
};
}
public function __sleep() {
$serializable = array();
foreach ( $this as $paramName => $paramValue ) {
if ( !is_string($paramValue) && !is_array($paramValue) && is_callable($paramValue) ) {
continue;
}
$serializable[] = $paramName;
}
return $serializable;
}
}
$foo = new foo();
echo serialize($foo);
About checking if value is instance of Closure class (from manual):
Anonymous functions are currently
implemented using the Closure class.
This is an implementation detail and
should not be relied upon.
Therefore I would implement is_closure($value) function as return !is_string($value) && !is_array($value) && is_callable($value) rather than return $value instanceof Closure and hope that some day PHP developers will add native is_closure() function.
Honestly, I think you're trying to solve the wrong problem. If you're sleeping the class, then isn't it wrong to have a successful sleep if you can't serialize everything? Otherwise you can wake up to an inconsistent state (or at least a state that's different than the current one). So I would argue that you should just put everything into the resultant array and then let PHP tell you if it's not serializable.
Otherwise, do you then need to check to see if any stored objects are serialzable? Should you then be checking for Serializable interface or the existence of __sleep? Where do you draw the line? So I would say that you should only not serialize resources and variables that you explicitly know how to recreate in the wakeup function (such as a database connection, or any closures you explicitly know how to recreate). But be careful here, since if you let those closures/resources be changed via the object's API, how can you be sure of a successful wakeup to the prior state.
So in short, I would recommend just returning everything, and letting PHP handle unserializable variables. Otherwise you'd need to either white-list (which isn't going to be practical) or black-list (which isn't going to be complete). And neither is a great solution. Just handle the exception when it comes (throwing and catching exceptions isn't bad).
As far as your exact question, I would implement it as follows:
function is_closure($callback) {
$func = function(){};
return $callback instanceof $func;
}
It still relies on the implementation detail of the closure being of a Object type, but I think that's the best we can do at this point. The best solution would be to petition the core to add a is_closure() function which would be implementation independent...