I'm trying to build a __construct function for my class. This function should get All $_REQUEST values and store them in an array so they can be called later by id. The problem is that it doesn't store my variables and I don't know why.
Seeing this is my first "serious" attempt at a construct, it's most probably my stupidity. But I'd like to know what that is.
Class Regex {
private static $requests = array();
function __construct() {
foreach($_REQUEST as $key => $value) {
self::$requests[$key] = array(
'value' => $value,
'status' => false,
'errorList' => array()
);
}
}
public static function preg($key, $rules) {
var_dump(self::$requests); // for test purpose
}
}
The result of above is: array (size=0) empty.
Are you even calling the constructor? A constructor is only called when calling it either explicitly or via the new keyword).
PHP doesn't have anything like static constructors like Java has.
You have to ensure that the array is filled at the first access to preg() method:
public static function preg($key, $rules) {
if (empty(self::$requests)) {
foreach($_REQUEST as $key => $value) {
self::$requests[$key] = array(
'value' => $value,
'status' => false,
'errorList' => array()
);
}
}
var_dump(self::$requests); // for test purpose
}
the constructor of your Regex class is called upon creating a new regex object like so:
$regex = new Regex;
but you never create a Regex object so the constructor is never called, resulting in an empty $requests array.
You work with a static function. I think you don't call the construct method. The __construct function is called if you make a new instance.
$regex = new Regex;
If you call the static class for example Regex::preg the contructor is not called.
Expanding upon what bwoebi has answered, you can still get the intended results simply by adding a call to the static function preg from the constructor itself as you can see bellow:
That said, this is just adding more bloat without any real benefit.
Whilst this could come in useful in certain cases, and I only added it within this answer as a mean to display that your current structure could work with the simple addition of two simple lines, I would recommend going with #bwoebi's answer but to keep in mind that whilst the controller is initself not static, it does not in any way or form stop it from communicating with static methods.
PHP:
class Regex {
private static $requests = array();
function __construct(){
if (empty(self::$requests)) {
foreach($_REQUEST as $key => $value) {
self::$requests[$key] = array(
'value' => $value,
'status' => false,
'errorList' => array()
);
}
}
self::preg();
}
public static function preg(){
var_dump(self::$requests); // for test purpose
}
}
$var = new Regex();
Related
When Instantiating a class, sometimes there are params/arguments that are passed to that class constructor
class MyLibrary{
function __construct($param1, $param2, $param3){
/*
* do something with those params
* Here im setting "something" to be $param3 instead of param1 nor param2
*/
$this->something = $param3; //I used param three to clarify my question
}
}
The class constructor relies on those argument to run so in Codeigniter, they state that arguments must be passed in a form of an array when using
$arg = ['param1' => '1', 'param2' => '2', 'param3' => '3'];
$this->load->library('MyLibrary', $arg, 'custom_name');
So my question is, how do I pass those params in a sequence that the constructor is expecting them in Codeigniter?
Your constructor will receive the array just in the same order as you filled it.
So when you do this:
$arg = ['param1' => '1', 'param2' => '2', 'param3' => '3'];
$this->load->library('MyLibrary', $arg);
Your constructor in your library must be like this:
public function __construct($args)
{
// Do something with $args like:
$this->var1 = $args['param1'];
$this->var2 = $args['param2'];
// etc
}
See docs for reference: https://www.codeigniter.com/user_guide/general/creating_libraries.html
Based on the array $arg you define above
$this->something = $arg['param3'];
If your question is "How do I define a custom class that accepts arguments to __construct that can be loaded using $this->load->library()?" The answer is to use an associative array for the argument. The `load' class only allows for a single argument so you have to use an array to pass multiple values.
All the core libraries use associative arrays to pass arguments to class constructors.
Your library must be defined like this.
/application/libraries/Custom_library.php
class Custom_library
{
public function __construct($args = array()){
//code here
}
}
If Custom_library requires three params then you need to create and pass an associative array with three name/value pairs. In a controller it might look like this.
class Example extends CI_Controller
{
public function __construct()
{
parent::__construct();
//create an associative array
$args = ['param1' => '1', 'param2' => '2', 'param3' => '3'];
//pass array to library
$this->load->library('custom_library', $args);
}
}
To work with the above controller a more complete version of Custom_library might be defined like this
class Custom_library
{
protected $foo;
protected $bar;
protected $baz;
public function __construct($args = array())
{
if(!empty($args))
{
$this->foo = $args['param1'];
$this->bar = $args['param2'];
$this->baz = $args['param3'];
}
}
}
As per your comment:
I understand this, so sometimes, those are third party Libraries and
we don't want to modify them, They are just plug and play libraries.
These libraries only expect you to pass those params
if its a third-party library that you don't want to modify like DFriend suggested in his answer.
in the model/controller/base controller:
require_once(APPPATH . '/third_party/somename.php);
and then new Somename($param1, $param2, $param3) where you need it.
This is not how things should be done in CI (again DFriends answer is the best if you want to modify), but I understand sometimes people don't want to modify the library so updating or whatever is easy and you can use normal php this way in CI.
I'm still new to OOP and this is probably a simple question, not sure if I'm overthinking this.
Let's say we have a simple class like the following that we can use to instantiate an object that can generate an array:
class gen_arr {
public $arr = array();
public function fill_arr() {
$this->arr["key"] = "value";
}
}
// instantiate object from gen_arr
$obj = new gen_arr();
Now if you wanted to get the value of the object's array's item, would you generate an array first and then echo the value like:
$arr = $obj->fill_arr();
echo $arr["key"];
Or would you access the object's property directly?
echo $obj->arr["key"]
In the actual code the property is private and there is a method that allows the viewing of the property array, the above is just to simplify the question.
Are there performance considerations and/or just best practices when it comes to this kind of case?
UPDATE:
It's still unclear from the answers if the best way is to generate an array from the property and access that array or just access the property directly (through the getter method)
Since you are filling the array with items only on fill_arr, those items wont be availabl until you call $arr = $obj->fill_arr();.
If you want to directly call the array, then you have to fill this array on the constructor function of this call like this:
class gen_arr {
public $arr = array();
function __construct() {
$this->arr["key"] = "value";
}
}
First off, the class you shared with us has a range of problems:
its sole instance property is public and can be modified by anyone
you have some temporal coupling, the method fill_arr() needs to be invoked before accessing the the value makes any sense
Encapsulation
Reduce the visibility of the instance property from public to private, so that the property can only be modified by the object itself, and provide an accessor instead:
class gen_arr
{
private $arr;
public function fill_arr()
{
$this->arr["key"] = "value";
}
public function arr()
{
return $this->arr;
}
}
Temporal Coupling
Remove the method fill_arr() and instead initialize the property $arr in one of the following options:
initialize field lazily when accessed the first time
initialize field in the constructor
initialize field with a default value
initialize field with a value injected via constructor
Initialize field lazily when accessed the first time
Initialize the field when it's accessed the first time:
class gen_arr
{
private $arr;
public function arr()
{
if (null === $this->arr) {
$this->arr = [
'key' => 'value',
];
}
return $this->arr;
}
}
Initialize field in the constructor
Assign a value during construction:
class gen_arr
{
private $arr;
public function __construct()
{
$this->arr = [
'key' => 'value',
];
}
public function arr()
{
return $this->arr;
}
}
Initialize field with a default value
Assign a value to the field directly, which works fine if you don't need to do any computation:
class gen_arr
{
private $arr = [
'key' => 'value',
];
public function arr()
{
return $this->arr;
}
}
Initialize field with a value injected via constructor
If the values are not hard-coded or otherwise calculated (as in the previous examples), and you need to be able to instantiate objects with different values, inject values via constructor:
class gen_arr
{
private $arr;
public function __construct(array $arr)
{
$this->arr = $arr;
}
public function arr()
{
return $this->arr;
}
}
Accessing and dereferencing values
This seems like this is your actual question, so the answer is - of course - It depends!.
Let's assume we have provided an accessor instead of accessing the otherwise public field directly:
Since PHP 5.4, the following is possible:
$object = new gen_arr();
echo $object->arr()['key'];
If you are still using an older version of PHP, you obviously can't do that and have to do something like this instead:
$object = new gen_arr();
$arr = $object->arr();
echo $arr['key'];
Largely, though, the answer to this question depends on the circumstances, and what you want to achieve. After all, readability is key for maintenance, so it might just make sense for you to introduce an explaining variable.
Note About your example, you could just use an ArrayObject instead:
$arr = new \ArrayObject([
'key' => 'value',
]);
echo $arr['key']);
For reference, see:
http://wiki.c2.com/?EncapsulationDefinition
http://blog.ploeh.dk/2011/05/24/DesignSmellTemporalCoupling/
http://php.net/manual/en/language.oop5.properties.php
http://wiki.c2.com/?ItDepends
http://php.net/manual/en/migration54.new-features.php
https://refactoring.com/catalog/extractVariable.html
http://wiki.c2.com/?IntroduceExplainingVariable
http://php.net/manual/en/class.arrayobject.php
For an example, see:
https://3v4l.org/qVVBM
First fill up the array
$gen_arr = new gen_arr();
$gen_arr->fill_arr();
then get the values with a getter method
$val = $gen_arr->getValue($key);
A getter method would be like this
public function getValue($key) {
return $this->arr[$key];
}
And certailny make the $arr property private
So I'm working with a bag class, and I'm trying to dynamically create variables from the bag class.
Here's what I have:
class BagFoo
{
public $a;
public $b;
public $c;
}
class Bar
{
private $bagFoo;
public function output()
{
foreach($this->bagFoo as $key => $value)
{
$$key = $value;
}
//output then happens here.
}
}
This allows me to call $a instead of $this->bagFoo->getA(); which I rather like, but the problem is I have to expose the member variables to implement it. I'd like to have the same dynamic variable assignment, but access the member variables through a getter, instead of accessing directly.
Solutions I've though of and didn't really like:
Having a getVars() function in BagFoo that would return an array of var names and their values, and then iterating through that.
Calling get_class_methods() and then doing parsing and iterating through the getters (ew).
I'm sure there's a way to do what I'm trying in a more elegant form, but I just can't think of how to implement it.
Your code would probably be more understandable if you just used an associative array to store your values.
class Bar
{
private $bagFoo = [];
public function __construct($arr)
{
$this->bagFoo = $arr;
foreach($arr as $key => $value)
{
$$key = $value;
}
echo $a; //echos 'aaaa'
}
}
$bar = new Bar([
'a' => 'aaaa',
'b' => 'bbbb',
'c' => 'cccc'
]);
Word of advice: Be very careful using $$ because you can overwrite variables in the current scope and cause all kinds of problems in your application. For example:
$bar = new Bar([
'_SERVER' => 'broken server vars!',
'_COOKIE' => 'broken cookies!',
'arr' => 'broken iterator!',
]);
I've been having a bit of trouble doing, what I believe is possible (although I'm not sure). What I do know is that what I'm attempting to do is a bit nonsensical and not the best way to do it.
class myClass {
public static function myData() {
$data = [
'index' => 'key'
];
}
}
What I'm attempting to do is to return the value of key via static function. I've been trying to view (via var_dump()) what's inside of my static function (myClass::myData();) however, it comes up NULL.
I'm still pretty new to PHP, but I've been working around trying to find things to work on (even if they're pretty nonsensical) to get better acquainted. If this is at all possible, I'd like to complete it this way. I've been searching for an answer to this for about 2 hours, so yes, I have looked around to try and fix this issue myself first, but to no avail.
Additionally, if this simply can't be done, what is the best way to do something like this? I appreciate any responses!
EDIT: Obviously I feel a bit foolish for not returning my data at the end of my function. Now when the class is dumped (via var_dump()), the information in my function is returned, however, I'm still not able to selectively return the data:
class myClass {
public static function myData() {
$data = [
'index' => 'key',
'index1' => 'key1'
];
return $data;
}
}
myClass::myData(); //here
I know the information is contained in the public static function, however, how would I selectively return key1 from index1
I really appreciate the help, everyone!
EDIT 2: I was able to figure it out with all of your help. Just a note, the example I'm using here, is obviously not the code I'm working with. The semicolon was a simple mistake, and is not in my actual code.
Here's the final product:
class myClass {
public static function myData() {
$d = [
'index' => 'key',
'index1' => 'key1'
];
return $data;
}
}
$data = myClass::myData();
print $data['index1']; // prints key1
Again, thanks for the help!
You do not have a return within your function you also have a syntax error within your array construct.
class myClass {
public static function myData() {
$data = [
'index' => 'key'; // The closing semi-colon is not necessary within the array
];
}
}
and to create a return:
return $data; // This will return the entire array
return $data['index']; // This will return a value of *key*
So, If i wanted to return the entire array:
class myClass {
public static function myData() {
$data = [
'index' => 'key'
];
return $data;
}
}
print_r(myClass::myData());
your function doesn't return anything, so try :
return $data;
A function can return data, but "data inside a function" have no sense.
I have an array in php like this:
$myArray = array('name'=>'juank', 'age'=>26, 'config'=>array('usertype'=>'admin','etc'=>'bla bla'));
I need this array to be accesible along the script to allow changes in any field EXCEPT in the "config" field. Is there a way to protect an array or part of an array from being modified as if it where declared private inside a class? I tried defining it as a constant but it's value changes during script execution. Implementing it as a class would mean I'd have to rebuild the complete application from scratch :S
thanks!
I do not think you can do this using "pure" "real" arrays.
One way to get to this might be using some class that implements ArrayInterface ; you code would look like it's using arrays... But it would actually be using objects, with accessor methods that could forbid write-access to some data, I guess...
It would have you change a couple of things (creating a class, instanciating it) ; but not everything : access would still be using an array-like syntax.
Something like this might do the trick (adapted from the manual) :
class obj implements arrayaccess {
private $container = array();
public function __construct() {
$this->container = array(
"one" => 1,
"two" => 2,
"three" => 3,
);
}
public function offsetSet($offset, $value) {
if ($offset == 'one') {
throw new Exception('not allowed : ' . $offset);
}
$this->container[$offset] = $value;
}
public function offsetExists($offset) {
return isset($this->container[$offset]);
}
public function offsetUnset($offset) {
unset($this->container[$offset]);
}
public function offsetGet($offset) {
return isset($this->container[$offset]) ? $this->container[$offset] : null;
}
}
$a = new obj();
$a['two'] = 'glop'; // OK
var_dump($a['two']); // string 'glop' (length=4)
$a['one'] = 'boum'; // Exception: not allowed : one
You have to instanciate an object with new, which is not very array-like... But, after that, you can use it as an array.
And when trying to write to an "locked" property, you can throw an Exception, or something like that -- btw, declaring a new Exception class, like ForbiddenWriteException, would be better : would allow to catch those specifically :-)
You could make the array private and create a method to modify its contents that will check if someone doesn't try to overwrite the config key.
<?php
class MyClass {
private static $myArray = array(
'config' => array(...),
'name' => ...,
...
);
public static function setMyArray($key, $value) {
if ($key != 'config') {
$this::myArray[$key] = $value;
}
}
}
Then when you want to modify the array you call:
MyClass::setMyArray('foo', 'bar'); // this will work
MyClass::setMyArray('config', 'bar'); // this will be ignored
No, unfortunately there isn't a way to do what you're describing. Variables don't have any concept of public or private unless they are encapsulated within an object.
Your best solution is unfortunately to re-work the configuration into an object format. You might be able to use a small object inside your array that contains the private settings, which might allow you to only have to update a few places in your code, depending where that portion of the array is used.