Is it possible to store value to attribute in class? If so then how?
For example:
class Student {
public $name;
public $grade;
function average() {
$sum = 0;
foreach ($this->grade as $key => $value) {
$sum += $value;
}
$avg = $sum/ count($key);
return $avg;
}
}
How to add values [math=8, history=9, biology=8] to attribute and develop method which count average of the grades?
You don't need to write any code in your class to assign values to $grade, because it's marked as public (see "Visibility" in the PHP documentation), which means code outside the class can access it.
After you create an instance of Student, you can just assign that array directly to the $grade property.
$student = new Student;
$student->grade = ['math'=> 8, 'history' => 9, 'biology'=> 8];
Instead, if you want to use that array as the default value for any new objects of this class, you can initialize the property to that value at the same time you declare it.
class Student {
public $name;
public $grade = ['math'=> 8, 'history' => 9, 'biology'=> 8];
// ...
Related
The following code snippet will produce an error on PHP 8.2:
<?php
const foo = new stdClass();
foo->bar = 'baz';
echo foo->bar;
?>
I would expect that an error would not occur, since I am assigning to the prop rather than trying to reassign the constant.
If I create a new class, extending stdClass, and add the following method:
class extendsStdClass extends stdClass {
public function set(string $name, mixed $value) {
$this->$name = $value;
}
}
then I can assign to props using the following syntax:
<?php
const foo = new extendsStdClass();
foo->set('bar', 'baz');
echo foo->bar;
?>
but, the linter will not recognize props being set in this way, nor provide any type hinting:
Undefined property: extendsStdClass::$bar
Is there some reason we are not able to write to props on a class instance that is defined as a constant?
I'd like to be able to define this const in a namespace, and access/modify it via
If you want to modify it, then it's not a const.
If you actually want a const, then you can just use an array to get key/value pairs:
namespace MyNamespace {
const FOO = [
'one' => 1,
'two' => 2,
];
}
Then reference it anywhere via:
print_r(\MyNamespace\FOO);
Yields:
Array
(
[one] => 1
[two] => 2
)
And you can pull out a single value with array notation:
echo \MyNamespace\FOO['one'];
If you want something that you can reference directly by namespace, and still modify, you could create a class with static storage. Sort of like a half-assed singleton:
namespace MyNamespace;
class Foo
{
private static array $data = [];
public static function get($name)
{
return self::$data[$name] ?? null;
}
public static function set($name, $value)
{
self::$data[$name] = $value;
}
}
Then you can set values from anywhere:
\MyNamespace\Foo::set('one', 1);
And get values from anywhere:
echo \MyNamespace\Foo::get('one');
I'm new to PHP and I'm not really familiar with classes, my question is how can I get the value of $quantity and pass it to public array which is within a class? My expected array output would be Bag => 10.
Here's my code:
$quantity = 10;
class QuantityLeft{
public array $inventory = [
'Bag' => [$quantity value here]
];
}
Option 1: provide the value through class constructor.
Option 2: Write a setter method and call it on the object
Below example implements both. The member variable should be protected against direct modification, and the setter makes validation of the input parameter to avoid illegal values.
class QuantityLeft {
protected $inventory = [];
public function __construct($quantity) {
$this->setQuantity($quantity);
}
public function setQuantity($quantity) {
if ($quantity < 0) {
throw new OutOfBoundsException('Quantity must be greater or equal 0');
}
$this->inventory['Bag'] = $quantity;
}
}
Usage:
$obj = new QuantityLeft(50);
or
$obj = new QuantityLeft(0);
$obj->setQuantity(50);
try
global $quantity =10
and than:
class QuantityLeft{
public array $inventory = [
'Bag' => global $quantity
];
}
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 know that in C# you can nowadays do:
var a = new MyObject
{
Property1 = 1,
Property2 = 2
};
Is there something like that in PHP too? Or should I just do it through a constructor or through multiple statements;
$a = new MyObject(1, 2);
$a = new MyObject();
$a->property1 = 1;
$a->property2 = 2;
If it is possible but everyone thinks it's a terrible idea, I would also like to know.
PS: the object is nothing more than a bunch of properties.
As of PHP7, we have Anonymous Classes which would allow you to extend a class at runtime, including setting of additional properties:
$a = new class() extends MyObject {
public $property1 = 1;
public $property2 = 2;
};
echo $a->property1; // prints 1
Before PHP7, there is no such thing. If the idea is to instantiate the object with arbitrary properties, you can do
public function __construct(array $properties)
{
foreach ($properties as $property => $value)
{
$this->$property = $value
}
}
$foo = new Foo(array('prop1' => 1, 'prop2' => 2));
Add variations as you see fit. For instance, add checks to property_exists to only allow setting of defined members. I find throwing random properties at objects a design flaw.
If you do not need a specific class instance, but you just want a random object bag, you can also do
$a = (object) [
'property1' => 1,
'property2' => 2
];
which would then give you an instance of StdClass and which you could access as
echo $a->property1; // prints 1
I suggest you use a constructor and set the variables you wish when initialising the object.
I went from c# to PHP too, so I got this working in PHP:
$this->candycane = new CandyCane(['Flavor' => 'Peppermint', 'Size' => 'Large']);
My objects have a base class that checks to see if there's one argument and if it's an array. If so it calls this:
public function LoadFromRow($row){
foreach ($row as $columnname=>$columnvalue)
$this->__set($columnname, $columnvalue);
}
It also works for loading an object from a database row. Hence the name.
Another way, which is not the proper way but for some cases okay:
class Dog
{
private $name;
private $age;
public function setAge($age) {
$this->age = $age;
return $this;
}
public function getAge() {
return $this->age;
}
public function setName($name) {
$this->name = $name;
return $this;
}
public function getName() {
return $this->name;
}
}
$dogs = [
1 => (new Dog())->setAge(2)->setName('Max'),
2 => (new Dog())->setAge(7)->setName('Woofer')
];