PhpStorm: extract() identifying variables - php

Does anyone know whether there is a setting in PhpStorm that can trigger identifying variables generated using extract() function?
Example would be something like the following:
/**
* #return array
*/
protected function orderSet() : array
{
//...
return [
'colour' => $colour,
'green' => $green,
'orange' => $orange
];
}
/**
* #test
*/
public function returns_correct_attribute_names()
{
$params = $this->orderSet();
extract($params);
$this->assertEquals(
'Colour',
$colour->name
);
}
At the moment any variable that's been extracted in the test is highlighted (unrecognised), but perhaps there is a setting that can change this behaviour?

The solution that LazyOne offered actually works. However there is a bit more context you need in order to implement it.
To accurately inform PHPSTORM about the variables you want to declare the comment must be placed directly above extract() and not the parent function.
public function db(){
$db = new SQLite3('db/mysqlitedb.db');
$payments = $db->query('SELECT * FROM payments');
while ($pay = $payments->fetchArray()){
/**
* #var string $to_user
* #var string $from_user
* #var number $amount
*/
extract($pay);
if (isset($to_user, $from_user, $amount))
echo "TO: {$to_user}| FROM: {$from_user}| $ {$amount} \n";
};
}
This is a working sample from my code ( couldn't copy yours for some reason ).
You can see just before I use the extract() function I declare in the comment block above it the hidden variables and data types.
Bonus: if you intend to use extract, I highly recommend you use an isset to ensure the array you're parsing contains the fields you are expecting.
example in code above

Related

In PHP classes is it possible to create a private variable inside of a function?

In using PHP classes I have noticed that inside a class when I define a variable in a function as a property of that class in the $this->variablename way it automatically becomes a public variable of that class.
class example {
public function setstring() {
$this->string = "string";
}
}
So that
$class = new example();
echo $class->string;
Outputs: string
However, in the case that I wanted to create private variables only accessible to functions inside the class, is there anyway to declare them only inside of the setstring() function? Instead of declaring them as private outside of the function like this.
class example {
private $string ='';
public function setstring() {
$this->string = "string";
}
}
The reasons someone might do this are for neatness, so as not to have a long list of private variables declared at the beggining of a class.
No, there is not a way to do that.
In PHP, you typically declare all your class/instance properties above your functions in alphabetical order with self-documenting comments. This is the most "neat" and clear way to write classes. It is also recommended that you avoid public properties entirely, using getters and setters as needed.
The canonical coding style for PHP is defined in PSR-1 and PSR-2. I also recommend that you check out PHPDoc.
Keep in mind, variables declared within the scope of your class method will be private to that method. You only need a class property if you plan to access it from other methods.
<?php
class Example {
/**
* Holds a private string
* #var string
*/
private $string = '';
/**
* Sets the private string variable
*/
public function setString() {
$this->string = 'This string is accessible by other methods';
$privateVar = 'This string is only accessible from within this method';
}
}
Not possible as a language feature, but there's a really simple and effective hack:
class Test
{
/**
* Hidden dynamic data.
* #var object
*/
private $_ = (object)[];
public function setString()
{
$this->_->string = "string";
}
}
You can change $_ into whatever you want, but it does look more hacky to me. $this->this is also an option.
How to create private variables in a PHP Class using OOP
(Object Oriented Programming).
The best way to declare a private variable in a PHP Class is to create them above the __Construction method, by convention you may start the variable with an underscore after the dollar sign (i.e $_private_variable) to let other programmers reading your codes know at sight that it is a private variable, brilliant!
Please declare all your variable in a class as private except the __getter and __setter which are always public, unless you have a reason not to do so.
Use the __setter (__set) function to set value(s) to your private variable inside a the class, and when the value is needed, use the __getter (__get) function to return the values.
To make sense of it, let' create a very small Class that could be use to create different type of Vehicles, this will give you and insight on how to properly create private variable, set values to it and return values from it, ready?
<?php
Class Vehicle{
/* Delcaration of private variables */
private $_name = "Default Vehicle";
private $_model;
private $_type;
private $_identification;
/* Declaration of private arrays */
private $_mode = array();
private $feature = array();
/* Magic code entry function, think of it as a main() in C/C++ */
public function __construct( $name, $model, $type ){
$this->create_vehicle( $name, $model, $type );
}
/* __getter function */
public function __get( $variable ){
if( !empty($this->$variable) ){
$get_variable = $this->$variable;
}
return $get_variable;
}
/* __setter function */
public function __set( $variable, $target ){
$this->$variable = $target;
}
/* Private function */
private function create_vehicle( $name, $model, $type ){
$this->__set( "_name", $name );
$this->__set( "_model", $model);
$this->__set( "_type", $type );
}
}
//end of the class.
?>
<?php
/* Using the Vehicle class to create a vehicle by passing
three parameters 'vehicle name', 'vehicle model', 'vehicle type'
to the class.
*/
$toyota = new Vehicle("Toyotal 101", "TY101", "Sedan");
/* Get the name and store it in a variable for later use */
$vehicle_name = $toyota->__get('_name');
/* Set the vehicle mode or status */
$vehicle_mode = array(
'gas' => 50,
'ignition' => 'OFF',
'tire' => "OK",
'year' => 2020,
'mfg' => 'Toyoda',
'condition' => 'New'
);
/* Create vehicle features */
$vehicle_feature = array(
"Tire" => 4,
"Horse Power" => "V6",
"blah blah" => "foo",
"Airbag" => 2,
"Transmission" => "Automatic"
//....
);
/* Create vehicle identification */
$vehicle_identification = array(
"VIN" => "0001234567ABCD89",
"NAME" => $vehicle_name,
"FEATURE" => $vehicle_feature,
"MODEL" => $vehicle_mode,
"YEAR" => 2020,
"MFG" => "Totota"
);
/* Set vehicle identification */
$toyota->__set("_identification", $vehicle_identification );
/* Set vehicle features */
$toyota->__set("_feature", $vehicle_feature );
/* Set vehicle mode */
$toyota->__set("_mode", $vehicle_mode);
/* Retrieve information and store them in variable using __get (getter) */
$vehicle_name = $toyota->__get('_name');
$vehicle_mode = $toyota->__get('_mode');
$vehicle_id = $toyota->__get('_identification');
$vehicle_features = $toyota->__get('_feature');
$vehicle_type = $toyota->__get('_type');
$vehicle_model = $toyota->__get('_model');
/* Printing information using store values in the variables. */
echo "Printing Vehicle Information\n";
echo "*****************************\n";
echo "Vehicle name is $vehicle_name \n";
echo "Vehicle Model is $vehicle_model \n";
echo "Vehich type is $vehicle_type \n";
printf("\n\n");
echo "Printing Vehicle Mode\n";
echo "***********************\n";
print_r( $vehicle_mode );
printf("\n\n");
echo "Printing Vehicle Features\n";
echo "**************************\n";
print_r( $vehicle_features );
printf("\n\n");
echo "Printing Vehicle Identification\n";
echo "******************************\n";
print_r( $vehicle_id );
printf("\n\n");
?>
The output of this code:
Printing Vehicle Information
*****************************
Vehicle name is Toyotal 101
Vehicle Model is TY101
Vehich type is Sedan
Printing Vehicle Mode
***********************
Array
(
[gas] => 50
[ignition] => OFF
[tire] => OK
[year] => 2020
[mfg] => Toyoda
[condition] => New
)
Printing Vehicle Features
**************************
Array
(
[Tire] => 4
[Horse Power] => V6
[blah blah] => foo
[Airbag] => 2
[Transmission] => Automatic
)
Printing Vehicle Identification
******************************
Array
(
[VIN] => 0001234567ABCD89
[NAME] => Toyotal 101
[FEATURE] => Array
(
[Tire] => 4
[Horse Power] => V6
[blah blah] => foo
[Airbag] => 2
[Transmission] => Automatic
)
[MODEL] => Array
(
[gas] => 50
[ignition] => OFF
[tire] => OK
[year] => 2020
[mfg] => Toyoda
[condition] => New
)
[YEAR] => 2020
[MFG] => Totota
)
To do a live test or experiment with this code, see the demo, change name, create new vehicle(s) as pleased.
I hope this helps.

Unit test: using the proper terminology for mocking/stubbing

After fundamental changes on my project system architecture, I find myself in a situation where I would need to create "fake" implementation in order to test some functionality that used to be public like the following:
/**
* Display the template linked to the page.
*
* #param $newSmarty Smarty object to use to display the template.
*
* #param $parameters associative Array containing the values to pass to the template.
* The key is the name of the variable in the template and the value is the value of the variable.
*
* #param $account child class in the AccountManager hierarchy
*
* #param $partialview String name of the partial view we are working on
*/
protected function displayPageTemplateSmarty(Smarty &$newSmarty, array $parameters = array(), AccountManager $account = NULL, string $partialview = "")
{
$this->smarty = $newSmarty;
if (is_file(
realpath(dirname(__FILE__)) . "/../../" .
Session::getInstance()->getCurrentDomain() . "/view/" . (
!empty($partialview) ?
"partial_view/" . $partialview :
str_replace(
array(".html", "/"),
array(".tpl", ""),
Session::getInstance()->getActivePage()
)
)
)) {
$this->smarty->assign(
'activeLanguage',
Session::getInstance()->getActiveLanguage()
);
$this->smarty->assign('domain', Session::getInstance()->getCurrentDomain());
$this->smarty->assign(
'languages',
Languagecontroller::$supportedLanguages
);
$this->smarty->assign(
'title',
Languagecontroller::getFieldTranslation('PAGE_TITLE', '')
);
$this->smarty->assign_by_ref('PageController', $this);
$htmlTagBuilder = HTMLTagBuilder::getInstance();
$languageController = LanguageController::getInstance();
$this->smarty->assign_by_ref('htmlTagBuilder', $htmlTagBuilder);
$this->smarty->assign_by_ref('languageController', $languageController);
if (!is_null($account)) {
$this->smarty->assign_by_ref('userAccount', $account);
}
if (!is_null($this->menuGenerator)) {
$this->smarty->assign_by_ref('menuGenerator', $this->menuGenerator);
}
foreach ($parameters as $key => $value) {
$this->smarty->assign($key, $value);
}
$this->smarty->display((!empty($partialview) ?
"partial_view/" . $partialview :
str_replace(
array(".html", "/"),
array(".tpl", ""),
Session::getInstance()->getActivePage()
)
));
}
}
In this case, the PageController class used to be called directly in controllers, but is now an abstract class extended by the controllers and my unit tests can no longer access the method.
I also have methods like this one in my new session wrapper class that can only be used in very specific context and for which I really need to create fake page implementation to test them.
/**
* Add or update an entry to the page session array.
*
* Note: can only be updated by the PageController.
*
* #param $key String Key in the session array.
* Will not be added if the key is not a string.
*
* #param $value The value to be added to the session array.
*
* #return Boolean
*/
public function updatePageSession(string $key, $value)
{
$trace = debug_backtrace();
$updated = false;
if (isset($trace[1]) and
isset($trace[1]['class']) and
$trace[1]['class'] === 'PageController'
) {
$this->pageSession[$key] = $value;
$updated = true;
}
return $updated;
}
Even though I read a few article, it is still quite unclear in my mind if those fake classes should be considered as "stub" or a "mock" (or even "fake", "dummy" and so on).
I really need to use the proper terminology since my boss is expecting me (in a close future) to delegate most of my workload with oversea developers.
How would you call those fake class implementation created solely for testing purpose in order to be self-explanatory?
Gerard Meszaros explains the terminology of dummies, stubs, spies, mocks, and fakes here.
You can find examples from the PHP world here.

Change variable value in CodeIgniter subview

I have a main view in my CI app in which I declare a variable.
That main view loads several subview in a loop.
I would like these subviews to modify my variable so that at the end of the loop its value would be updated.
Example:
main_view.php
<?php
$my_var = false;
foreach($a_views as $s_view){
$this->load->view($s_view, array('my_var' => $my_var);
}
var_dump($my_var); // Still false
?>
sub_view.php
<?php $my_var = true; ?>
Check out the code for the $this->load->view function.
The data you pass to the view is "one-way" which means it's available and can be modified only in the view itself.
Which means you'll have to find another way to achieve what you're trying to achieve.
Keep in mind that according to what I've understood from your question you're trying to have some "logical manipulation" using "views" which is kind of breaking the MVC architecture.
/**
* View Loader
*
* Loads "view" files.
*
* #param string $view View name
* #param array $vars An associative array of data
* to be extracted for use in the view
* #param bool $return Whether to return the view output
* or leave it to the Output class
* #return object|string
*/
public function view($view, $vars = array(), $return = FALSE)
{
return $this->_ci_load(array('_ci_view' => $view, '_ci_vars' => $this->_ci_prepare_view_vars($vars), '_ci_return' => $return));
}

Unserialize() offset error when using CodeIgniter 2.2 Sessions with Objects

I'm trying to debug some old code from CodeIgniter 2.2. When running some data thru Session, I noticed an unserialize error, Message: unserialize(): Error at offset 160 of 163 bytes. After doing some debugging and research, I found out it's a common backslash issue when unserializing data from Sessions.
The serialized data I'm using has objects of data with backslashes in them, which causes the errors to occur. I'm in need of a replacement that can handle standard class objects as well.
Could someone recommend a quick replacement for codeigniter's Session _serialize() and _unserialize() methods?
public function data_test() {
$input = array(
(object)array('name' => 'test2', 'desc' => 'bla bla ob/gyn'),
(object)array('name' => 'test2', 'desc' => 'bla bla ob\\gyn'),
);
var_dump($input);
$data = $this->_serialize($input);
var_dump($data);
$result = $this->_unserialize($data);
var_dump($result);
}
// --------------------------------------------------------------------
/**
* Serialize an array
*
* This function first converts any slashes found in the array to a temporary
* marker, so when it gets unserialized the slashes will be preserved
*
* #access private
* #param array
* #return string
*/
function _serialize($data) {
if (is_array($data)) {
foreach ($data as $key => $val) {
if (is_string($val)) {
$data[$key] = str_replace('\\', '{{slash}}', $val);
}
}
} else {
if (is_string($data)) {
$data = str_replace('\\', '{{slash}}', $data);
}
}
return serialize($data);
}
// --------------------------------------------------------------------
/**
* Unserialize
*
* This function unserializes a data string, then converts any
* temporary slash markers back to actual slashes
*
* #access private
* #param array
* #return string
*/
function _unserialize($data) {
$data = unserialize(strip_slashes($data));
if (is_array($data)) {
foreach ($data as $key => $val) {
if (is_string($val)) {
$data[$key] = str_replace('{{slash}}', '\\', $val);
}
}
return $data;
}
return (is_string($data)) ? str_replace('{{slash}}', '\\', $data) : $data;
}
/**
* Serialize an array
*
* This function serializes the data and then base64_encodes it for
* storage with memcached. This avoids the common backslash issue.
*
* #access private
* #param array
* #return string
*/
function _serialize($data) {
return base64_encode(serialize($data));
}
// --------------------------------------------------------------------
/**
* Unserialize
*
* This function unserializes a data string. I first base64_decodes
* the data from memcached storage.
*/
function _unserialize($data) {
return unserialize(base64_decode($data));
}
You can sometimes come across this issue if you are using different versions of PHP, or if you change the version of PHP you are using while a session was open.
For example if you have a session cookie with an app that uses PHP 5.6.* and then you try to use it with an app (that resides on another sub-domain) that uses PHP 7.2.*, then you are going to get a warning error. Or, if you had an open session and then you changed the version of PHP that you are using with your app (say if you are developing locally and switching around PHP versions), then you'll get the warning. So best to use serialize/unserialize and with PHP version that does not change.

PHP memory references

I am wondering this question for a long time, how does PHP handle references are they a good idea to use and I can't explain better than using an example, lets look at the following class and then # the comment of the setResult method.
Lets imagine we are using a model view controller framework and we are building a basic AjaxController, we only got 1 action method (getUsers) so far. Read the comments, and I hope my question is clear, how does PHP handle these kind of situations and is it true what I wrote about the x times in the memory # the setResult docblock.
class AjaxController{
private $json = array(
'result' => array(),
'errors' => array(),
'debug' => array()
);
/**
* Adds an error, always displayed to users if any errors.
*
* #param type $description
*/
private function addError($description){
$this->json['errors'][] = $description;
}
/**
* Adds an debug message, these are displayed only with DEBUG_MODE.
*
* #param type $description
*/
private function addDebug($description){
$this->json['debug'][] = $description;
}
/**
* QUESTION: How does this go in memory? Cause if I use no references,
* the array would be 3 times in the memory, if the array is big (5000+)
* its pretty much a waste of resources.
*
* 1st time in memory # model result.
* 2th time in memory # setResult ($resultSet variable)
* 3th time in memory # $this->json
*
* #param array $resultSet
*/
private function setResult($resultSet){
$this->json['result'] = $resultSet;
}
/**
* Gets all the users
*/
public function _getUsers(){
$users = new Users();
$this->setResult($users->getUsers());
}
public function __construct(){
if(!DEBUG_MODE && count($this->json['debug']) > 0){
unset($this->json['debug']);
}
if(count($this->json['errors']) > 0){
unset($this->json['errors']);
}
echo json_encode($this->json);
}
}
Another simple example: What would be better to use technique A:
function example(){
$latestRequest = $_SESSION['abc']['test']['abc'];
if($latestRequest === null){
$_SESSION['abc']['test']['abc'] = 'test';
}
}
Or technique B:
function example(){
$latestRequest =& $_SESSION['abc']['test']['abc'];
if($latestRequest === null){
$latestRequest = 'test';
}
}
Thanks for reading and advise :)
In short: don't use references.
PHP copies on write. Consider:
$foo = "a large string";
$bar = $foo; // no copy
$zed = $foo; // no copy
$bar .= 'test'; // $foo is duplicated at this point.
// $zed and $foo still point to the same string
You should only use references when you need the functionality that they provide. i.e., You need to modify the original array or scalar via a reference to it.

Categories