I wanted to have a static closure variable in my class, so that people can change the behavior of a specific part of the code. However, I can't seem to be able to initialize it anywhere.
First I tried this:
public static $logger = function($sql) { print_r($sql); };
But apparently PHP can't handle that. Ok, so I made a static init method:
public static $logger;
static function init() {
/* if (!Base::logger) */
Base::logger = function($sql) { print_r($sql); };
}
And call it at the end of the file, outside class definition. But this also give me a syntax error: Parse error: syntax error, unexpected '=' in [file] on line [line]. Any hints?
The syntax error is right where the error message tells you it is (it would have been even easier to spot if you had given us line numbers...): a missing $-sign.
Base::$logger = function (...)
In addition to that, you migth want to use self:: instead of Base::, this ensure the code will work without any additional changes if you ever rename the class
self::$logger = function (...)
You can improve this code even further, when changing the initializer to a getter that JIT-creates the closure:
private static $logger = NULL;
public static function getLogger () {
if (self::$logger === NULL) {
self::$logger = function ($sql) {print_r($sql);};
}
return self::$logger;
}
[Edit] Based on your comment on this: the clean OOP way of being able to change $logger would be to use a setter:
public static function setLogger ($closure) {
self::$logger = $closure;
}
COmbining this and the getter from above ensures that you always get the value set by the setter, and, if none has been set yet, the default value. Using the setter to set the value back to NULL makes the getter create the default again, which is anoth er plus.
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 have following code
class DataMapperFactoryBeta
{
static private $configClassName = 'ConfigBeta';
static private $client;
static private $mapper = [];
static public function initClient()
{
$className = 'Models\\DataMappers\\Clients\\'.self::$configClassName::$db_type;
}
}
The Interpretor throws me a fatal error: 'incorrect access to static class member'. I wish to have the config class name accessed dynamicly, because I will change it in the future and I don't wanna change it in many places in the code, only once, through $configClassName. Is this even possible with statics?
Split your line into two, and it should work for you as you expect:
$className = 'Models\\DataMappers\\Clients\\' . self::$configClassName;
$className = $className::$db_type;
On a side note, I couldn't find in the PHP docs whether the scope resolution operator (::) is left or right associative. It could be it's trying to interpret the line as follows:
('Models\\DataMappers\\Clients\\'.self::($configClassName::$db_type));
Without an update from the docs the code is ambiguous as to what exactly should be happening the way you have it written.
Here's my test:
<?php
require __DIR__ . '/vendor/autoload.php';
class HasStatic {
public static function static_method() {
return true;
}
}
class SUT {
public $has_static;
public function __construct() {
$this->has_static = new HasStatic();
}
public function call_static() {
// A parse error :<
// $this->has_static::static_method();
$has_static = $this->has_static;
return $has_static::static_method();
}
}
class PhpStaticCallOnProperty extends PHPUnit_Framework_TestCase {
public function testPhpStaticCallOnProperty() {
$sut = new SUT();
$this->assertTrue($sut->call_static(), 'call_static() succeeded');
}
}
As you can see, I discovered that $this->has_static::static_method(); yields a parse error.
Is there a clean way to make this call without the extra assignment?
Static methods are black boxes of functionality where you explicitly define everything going in (parameters) and out (return value). As such, they are not tied to an object - and you shouldn't call them using an object reference. static_method() should only ever be called using HasStatic::static_method(), or self::static_method() from within the HasStatic class.
There's nothing inherently wrong with static methods - I strongly disagree with tereško saying they should be avoided. If a method doesn't need an object's context, it may as well be static.
The parse error occurs because there's no reason to use the scope resolution operator (::) on a property. Variable class names do mean the following will work:
$foo = 'HasStatic';
$foo::static_method(); // equivalent to HasStatic::static_method()
However that variable cannot be a property - you'll have to assign it to a temporary variable if you want to call the method in this way.
The Situation
I have a table in a DB that contains job types, basically defined by a label and a price. I'm trying to do a simple SELECT * FROM jobtype but I can't seem to get it to work, although I've use the same block over and over again in the rest of the code. The main difference here, is that it is a singleton trying to execute the function.
The problem is that as soon as I uncomment the line $rs_job_types = mysql_query($query_job_types, $vtconnection) or die(mysql_error()); the page will stop loading at this particular point in the code.
The Code
Following is the code of my function getJobTypes():
require_once('Connections/vtconnection.php');
class JobTypes extends Singleton{
static private $job_types;
public static function getJobTypes(){
if (self::$job_types == null){
echo 'DEBUG: For now, $job_types is NULL.'."\n";
mysql_select_db($database_vtconnection, $vtconnection);
$query_job_types = 'SELECT * FROM jobtype';
$rs_job_types = mysql_query($query_job_types, $vtconnection) or die(mysql_error());
while ($rs_row = mysql_fetch_assoc($rs_job_types)){
// let the job type identifier in the db be its index in our array
self::$job_types[$rs_row['id']]['label'] = $rs_row['label']; // job type label
self::$job_types[$rs_row['id']]['price'] = $rs_row['price']; // job type price
}
if (self::$job_types != null) echo 'DEBUG: $job_types has been populated.'."\n";
}
return self::$job_types;
}
}
Which I am calling like so:
$jt = JobTypes::getJobTypes();
Here is my singleton pattern:
class Singleton{
private static $instances = array();
final private function __construct(){
}
final public function __clone(){
trigger_error('You don\'t clone a singleton!', E_USER_ERROR);
}
final public static function getInstance(){
$c = get_called_class();
if(!isset(self::$instances[$c])){
self::$instances[$c] = new $c;
}
return self::$instances[$c];
}
}
I have turned the problem in my head, commented everything inside the getJobtypes() function and uncommented step by step. I found out the problem does happen with the mysql_query() line, just can't seem to work out why. Is something obviously wrong in my code?
Solved
As suggested here, I used global $vtconnection,$database_vtconnection; at the start of my static function and all went smooth. It is not an optimal solution but it pointed out the scope issue which I will now try to resolve.
I also got rid of the singleton pattern.
Well the most obvious thing is $database_vtconnection $vtconnection are defined nowhere in your getJobTypes function. If they're part of the class, you need $this (object) or self (static) references. More likely it looks like you're trying to use global variables, in which case you have to pull them into the scope of the function.
mysql_select_db can auto-connect (if $vtconnection isn't supplied) but only if it knows how - there's a previous connection (or possible INI config with db/host/user/pass).
To pull them into scope you need to add the line at the beginning of the function:
global $vtconnection,$database_vtconnection;`
... Or use the $GLOBALS superglobal array:
mysql_select_db($GLOBALS["database_vtconnection"],$GLOBALS["vtconnection"]);
For the record using globals isn't a particularly good solution.
T_PAAMAYIM_NEKUDOTAYIM sounds really exotic, but most certainly absolutely nonsense to me. I traced it all down to this lines of code:
<?php
Class Context {
protected $config;
public function getConfig($key) { // Here's the problem somewhere...
$cnf = $this->config;
return $cnf::getConfig($key);
}
function __construct() {
$this->config = new Config();
}
}
?>
In the constructor I create a Config object. Here's the class:
final class Config {
private static $instance = NULL;
private static $config;
public static function getConfig($key) {
return self::$config[$key];
}
public static function getInstance() {
if (!self::$instance) {
self::$instance = new Config();
}
return self::$instance;
}
private function __construct() {
// include configuration file
include __ROOT_INCLUDE_PATH . '/sys/config/config.php'; // defines a $config array
$this->config = $config;
}
}
No idea why this doesnt work / what the error means...
T_PAAMAYIM_NEKUDOTAYIM is the double colon scope resolution thingy PHP uses - ::
Quick glance at your code, I think this line:
return $cnf::getConfig($key);
should be
return $cnf->getConfig($key);
The first is the way to call a method statically - this code would be valid if $cnf contained a string that was also a valid class. The -> syntax is for calling a method on an instance of a class/object.
Just my two cents for future visitors who have this problem.
This is the correct syntax for PHP 5.3, for example if you call static method from the class name:
MyClassName::getConfig($key);
If you previously assign the ClassName to the $cnf variable, you can call the static method from it (we are talking about PHP 5.3):
$cnf = MyClassName;
$cnf::getConfig($key);
However, this sintax doesn't work on PHP 5.2 or lower, and you need to use the following:
$cnf = MyClassName;
call_user_func(array($cnf, "getConfig", $key, ...otherposibleadditionalparameters... ));
Hope this helps people having this error in 5.2 version (don't know if this was openfrog's version).
In your example
return $cnf::getConfig($key)
Probably should be:
return $cnf->getConfig($key)
And make getConfig not static
if you still need to use the double-colon then make sure your on PHP 5.3+
The error is down to an "inappropriate use" of the double colon operator:
return $cnf::getConfig($key);
as by using the :: you're attempting to call a static method of the class itself. In your example you want to call a non-static method on an instantiated object.
I think what you want is:
return $cnf->getConfig($key);
According to wikipedia, it means a "double colon" scope resolution operator.
http://en.wikipedia.org/wiki/Scope_resolution_operator
It's the name for the :: operator
Wikipedia
For anyone using Laravel. I was having the same error on Laravel 7.0. The error looked like this
syntax error, unexpected '::' (T_PAAMAYIM_NEKUDOTAYIM), expecting ';' or ','
It was in my Routes\web.php file, which looked like this
use Illuminate\Support\Facades\Route;
use Illuminate\Http\Request;
use // this was an extra **use** statement that gave me the error
Route::get('/', function () {
return view('save-online.index');
})->name('save-online.index');