Consider the following array of objects:
class Person {
public $name;
public $occupation;
public function __construct($n, $o){
$this->name = $n;
$this->occupation = $o;
}
}
$people = array(
new Person("John", "singer"),
new Person("Paul", "guitar"),
new Person("George", "bass"),
new Person("Ringo", "drums")
);
Is there any quick way to access the objects? I wouldn't mind storing them in a different datatype (as opposed to array) if another datatype could make access easier.
Example of accessing an object: I would like to now change the "Paul" object to have an occupation of singer. This is the current solution:
foreach ( $people as &$p ) {
if ( $p->name=="Paul" )
$p->occupation="singer";
}
Alternatively, I might need to access based on a different property: Let's change all the singers' names to Yoko:
foreach ( $people as &$p ) {
if ( $p->occupation=="singer" )
$p->="Yoko";
}
Another example of accessing an object, this time in order to get the occupation of Ringo:
$ringosOccupation="";
foreach ( $people as $p ) {
if ( $p->name=="Ringo" )
$ringosOccupation = $p->occupation;
}
I suppose that I could write a People class that stores each Person object in an internal array and supplies functions to change or read occupation, but if PHP has anything cleverer build in I would love to know.
Thanks.
Just index your array with the names:
$people = array(
"John" => new Person("John", "singer"),
"Paul" => new Person("Paul", "guitar"),
"George" => new Person("George", "bass"),
"Ringo" => new Person("Ringo", "drums")
);
// Paul is the new drummer:
$people["Paul"]->occupation = "drums";
It creates a little bit of redundancy, but surely that redundancy won't be more memory or compute intensive than looping over all of them to locate the one you need every time you need to modify something.
Update:
After the question was updated, it is clear that names may be non-unique or other properties needed for access. In that case, you might be better off using a database to store object state if you have to do it often. You can't escape needing to iterate over the array if it can't be uniquely indexed. It is trivially easy to make these changes in a database, but you would need to be rebuilding the objects all the time.
So, if your array is not too large, keep looping like you have been. If it meets your performance needs, its an ok method. If you have lots and lots of these to modify, and modify often, I would suggest storing them in a database and building the objects only when you need to read one out. Then you could do:
UPDATE people SET name = 'Yoko' WHERE occupation = 'singer'
Why aren't you just setting the key of the elements to the name?
$people = array(
'john' => new Person("John", "singer"),
'paul' => new Person("Paul", "guitar"),
'george' => new Person("George", "bass"),
'ringo' => new Person("Ringo", "drums"),
);
Related
I want to use $_SESSION to store items in cart. The items are defined by id, each item has 3 sizes and for each size there will be stored item's quantity. I would like to use multidimensional associative array like that
$_SESSION['cart']['id'.$_GET['id']]['size'.$_POST['size']]['quantity'] += $_POST['quantity'];
but I guess the problem which I am getting (Notice: Undefined index) is because the arrays are not defined first.
I would like to keep it simple, so what would be the easiest way?
Your issue is that you're just assuming the items are set in $_SESSION. You need to assume they aren't and start by adding them in.
You'd harness isset().
if(!isset($_SESSION['cart']['id'.$_GET['id']])) {
$_SESSION['cart']['id'.$_GET['id']] = array(
'sizeONE' => array(
'quantity' => 0
),
'sizeTWO' => array(
'quantity' => 0
),
'sizeTHREE' => array(
'quantity' => 0
),
);
}
You'd obviously modify the above to probably only set the product id as you require then run through the same sort of isset() to add the selected sizes. I'm just showing you how to create the initial structure array.
I'd argue the best manner to store this data isn't in a multidimensional array, but rather in an Object (and not in $_SESSION, but that's a whole different topic).
If you want to approach this with an object, I'd use a variation of the following:
$myItem = new stdClass;
$myItem->id = $_GET['id'];
$myItem->sizeOne = new stdClass;
$myItem->sizeOne->name = "sizeOne";
$myItem->sizeOne->quantity = 1;
// repeat for sizeTwo and sizeThree
$_SESSION['cart'][$myItem->id] = $myItem;
Benefits:
More of an OOP approach to your program.
Drawbacks:
Storing an Object in the $_SESSION may cause scalability issues.
I'm building a data structure for a Mustache template. I can easily build it using an array. But since I'm trying to learn more about working with Objects, I want to use an Object instead.
$mySlider = array("title" => "Photos of Las Vegas");
$mySlider = array("items" => array());
foreach ( $query as $row ) {
$newItem = [];
$newItem["postTitle"] = $row["imageTitle"];
$newItem["postURL"] = $row["imageURL"];
}
array_push($mySlider["items"], $newItem);
This is my current solution. I don't like it, too much code. By using an Object of "Slider" I'd like to be able to do something like this:
$mySlider = new PhotoSlider("Photos of Las Vegas");
foreach ( $query as $row ) {
$newPhoto = $mySlider->newPhoto();
$newPhoto->addProperty("imageTitle", $row["imageTitle"]);
$newPhoto->addProperty("imageURL", $row["imageURL"]);
}
I know I just made this up but this would be much nicer.
How do I need to write this Object in PHP to make it work this way with only one Class? I got stuck when I noticed I'd need another Object for the "newPhoto()" element, since that would need some methods of its own to do "addProperty", for instance.
I'd appreciate any pointers. Thanks!
You will need to create 2 classes, one for PhotoSlider and one for Photo. Photoslider would have array of Photos, and methods like add the Photo objects to the PhotoSlider, delete etc. Photo class would have the set attribute methods. Like:
$mySlider = new PhotoSlider("Photos of Las Vegas");
foreach ( $query as $row ) {
$newPhoto = new Photo();
$newPhoto->addProperty("imageTitle", $row["imageTitle"]);
$newPhoto->addProperty("imageURL", $row["imageURL"]);
$mySlider->addPhoto($newPhoto);
}
Usually when I search for one related ID I do it like this:
$thisSearch = $collection->find(array(
'relatedMongoID' => new MongoId($mongoIDfromSomewhereElse)
));
How would I do it if I wanted to do something like this:
$mongoIdArray = array($mongoIDfromSomewhereElseOne, $mongoIDfromSomewhereElseTwo, $mongoIDfromSomewhereElseThree);
$thisSearch = $collection->find(array(
'relatedMongoID' => array( '$in' => new MongoId(mongoIdArray)
)));
I've tried it with and without the new MongoId(), i've even tried this with no luck.
foreach($mongoIdArray as $seprateIds){
$newMongoString .= new MongoId($seprateIds).', ';
}
$mongoIdArray = explode(',', $newMongoString).'0';
how do I search '$in' "_id" when you need to have the new MongoID() ran on each _id?
Hmm your rtying to do it the SQL way:
foreach($mongoIdArray as $seprateIds){
$newMongoString .= new MongoId($seprateIds).', ';
}
$mongoIdArray = explode(',', $newMongoString).'0';
Instead try:
$_ids = array();
foreach($mongoIdArray as $seprateIds){
$_ids[] = $serprateIds instanceof MongoId ? $seprateIds : new MongoId($seprateIds);
}
$thisSearch = $collection->find(array(
'relatedMongoID' => array( '$in' => $_ids)
));
That should produce a list of ObjectIds that can be used to search that field - relatedMongoID.
This is what I am doing
Basically, as shown in the documentation ( https://docs.mongodb.org/v3.0/reference/operator/query/in/ ) the $in operator for MongoDB in fact takes an array so you need to replicate this structure in PHP since the PHP driver is a 1-1 with the documentation on most fronts (except in some areas where you need to use an additional object, for example: MongoRegex)
Now, all _ids in MongoDB are in fact ObjectIds (unless you changed your structure) so what you need to do to complete this query is make an array of ObjectIds. The ObjectId in PHP is MongoId ( http://php.net/manual/en/class.mongoid.php )
So you need to make an array of MongoIds.
First, I walk through the array (could be done with array_walk) changing the values of each array element to a MongoId with the old value encapsulated in that object:
foreach($mongoIdArray as $seprateIds){
$_ids[] = $serprateIds instanceof MongoId ? $seprateIds : new MongoId($seprateIds);
}
I use a ternary operator here to see if the value is already a MongoId encapsulated value, and if not encapsulate it.
Then I add this new array to the query object to form the $in query array as shown in the main MongoDB documentation:
$thisSearch = $collection->find(array(
'relatedMongoID' => array( '$in' => $_ids)
));
So now when the query is sent to the server it forms a structure similar to:
{relatedMongoId: {$in: [ObjectId(''), ObjectId('')]}}
Which will return results.
Well... I came across the same issue and the solution might not be relevant anymore since the API might have changed. I solved this one with:
$ids = [
new \MongoDB\BSON\ObjectId('5ae0cc7bf3dd2b8bad1f71e2'),
new \MongoDB\BSON\ObjectId('5ae0cc7cf3dd2b8bae5aaf33'),
];
$collection->find([
'_id' => ['$in' => $_ids],
]);
I have a problem. I have a website with people and different transactions they make to and from a fake online bank. I want to be able to store an array of each person's transactions on my mysql database. I want each transaction to be defined as an associative array with a timestamp and the sql query that represents their transaction with the "bank".
Then I want those, after being serialized, to be the values of a transactions array that holds all of their transactions. Then I want to serialize that and store it in the database so that later I can add a transaction by unserializing it and appending a serialized array of another transaction to it. So far this code below works except that it just overwrites the one transaction and doesn't append a new one. I'd really appreciate any help.
Thanks in advance
function modify_transactions($row, $sql)
{
$sql=mysql_real_escape_string($sql);
if(isset($row["TRANSACTIONS"]))
{
$transactions = unserialize($row["TRANSACTIONS"]);
}
else
{
$transactions = array();
}
$transaction_array = array("timestamp"=>time(),"query"=>$sql);
$transaction_data = serialize($transaction_array);
$transactions[] = $transaction_data;
$transactions_upload = serialize($transactions);
$name = $row["NAME"];
$query = "UPDATE band.students SET TRANSACTIONS = '$transactions_upload' WHERE students.NAME = '$name'";
mysql_query($query);
}
If I were you, I'd rather go for a new table where every entry would represent a transaction and that had a foreign key student_id.
That'd be much, much, much cleaner and more flexible and scalable (i.e. what if you want to show the last 3 transactions of user X? What if a user had several million transactions?).
First, you don't need to serialize each array, then serialize again. Serialize is recursive:
$array = array(
array(
'1',
array()
),
array(
'2',
array()
)
);
$serialized = serialize($array);
$unserialized = unserialize($serialized);
echo "<pre>";
print_r($unserialized);
echo "</pre>";
Prints:
Array
(
[0] => Array
(
[0] => 1
[1] => Array
(
)
)
[1] => Array
(
[0] => 2
[1] => Array
(
)
)
)
So just serialize right before inserting into the database.
Second, you should change your database structure. Like vzwick mentioned, create a new table with a foreign key of the student. That way each entry represents a transaction.
Also, why are you storing the actual SQL query? That doesn't make any sense to me. Why don't you actually make a fake transaction?
Here's what I want to do:
$clsName = substr(md5(rand()),0,10); //generate a random name
$cls = new $clsName(); //create a new instance
function __autoload($class_name)
{
//define that instance dynamically
}
Obviously this isn't what I'm actually doing, but basically I have unknown names for a class and based on the name, I want to generate the class with certain properties etc.
I've tried using eval() but it is giving me fits over private and $this-> references...
//edit
Ok, obviously my short and sweet "here's what I want to do" caused massive strife and consternation amongst those who may be able to provide answers. In the hope of getting an actual answer I'll be more detailed.
I have a validation framework using code hints on the site I maintain. Each function has two definitions
function DoSomething($param, $param2){
//code
}
function DoSomething_Validate(vInteger $param, vFloat $param2){
//return what to do if validation fails
}
I'm looking to add a validator for primary keys in my database. I don't want to create a separate class for EVERY table (203). So my plan was to do something like
function DoSomething_Validate(vPrimaryKey_Products $id){ }
Where the __autoload would generate a subclass of vPrimaryKey and set the table parameter to Products.
Happy now?
As of PHP 7.0, with a little creativity and knowledge of some lesser known PHP features, you can absolutely do this without resorting to eval or creating script files dynamically. You just need to use anonymous classes and class_alias(), like such:
spl_autoload_register(function ($unfoundClassName) {
{
$newClass = new class{}; //create an anonymous class
$newClassName = get_class($newClass); //get the name PHP assigns the anonymous class
class_alias($newClassName, $unfoundClassName); //alias the anonymous class with your class name
}
This works because anonymous classes are still assigned a name behind the scenes and put in the global scope, so you're free to grab that class name and alias it. Check out the second comment under the anonymous classes link above for more information.
Having said that, I feel like all the people in this question who are saying "Eval is always a very bad idea. Just don't use it ever!" are just repeating what they've heard from the hive mind and not thinking for themselves. Eval is in the language for a reason, and there are situations where it can be used effectively. If you're on an older version of PHP, eval might be a good solution here.
However, they are correct in that it can open up very large security holes and you have to be careful how you use it and understand how to eliminate the risks. The important thing is, much like SQL injection, you have to sanitize any input you put in the eval statement.
For example, if your autoloader looked like this:
spl_autoload_register(function ($unfoundClassName) {
{
eval("class $unfoundClassName {}");
}
A hacker could do something like this:
$injectionCode = "bogusClass1{} /*insert malicious code to run on the server here */ bogusClass2";
new $injectionCode();
See how this has the potential to be a security hole? Anything the hacker puts between the two bogusClass names will be run on your server by the eval statement.
If you adjust your autoloader to check the class name passed in (i.e. doing a preg_match to make sure there's no spaces or special characters, checking it against a list of acceptable names, etc.), you can eliminate these risks and then eval might be totally fine to use in this situation. If you're on PHP 7 or higher though, I recommend the anonymous class alias method above.
its funny, actually this is one of the few things where eval doesnt seem such a bad idea.
as long as you can ensure that no user input will ever enter the eval.
you still have downsides like when your using a bytecode cache that code wont be cached etc etc. but the security issues of eval are pretty much related to having user inputy in the eval, or to ending up in the wrong scope.
if you know what you are doing, eval will help you with this.
That said, in my opinion you are much better off when you no rely on type-hinting for your validation, but you have one function
DoSomething_Validate($id)
{
// get_class($id) and other validation foo here
}
I know this is an old question and there are answers that WILL work, but I wanted to offer a few snippets that would answer the original question and I think offer a more expanded solution should someone end up here like I did when searching for an answer to this problem.
Create Single Dynamic Class
<?php
// Without properties
$myclassname = "anewclassname";
eval("class {$myclassname} { }";
// With a property
$myclassname = "anewclassname";
$myproperty = "newproperty";
eval("class {$myclassname} { protected \${$myproperty}; }";
?>
As long as you properly escape your text, you could also add a function in there.
But what about if you want to dynamically create classes based on something that could itself be dynamic such as creating a class for each table in your database as the original question mentioned?
Create Multiple Dynamic Classes
<?php
// Assumes $dbh is a pdo connection handle to your MySQL database
$stmt=$dbh->prepare("show tables");
$stmt->execute();
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
$handle = null;
$classcode = '';
foreach ($result as $key => $value) {
foreach ($value as $key => $value) {
$classcode = "class {$value} { ";
$stmt2=$dbh->prepare("DESC $value");
$stmt2->execute();
$result2 = $stmt2->fetchAll(PDO::FETCH_ASSOC);
foreach ($result2 as $key => $value) {
$classcode .= "public \${$value['Field']}; ";
}
$classcode .= "}";
eval($classcode);
}
}
?>
This will dynamically generate a class for each table in a database. For each class, a property that is named after each column will ALSO get created.
Now it's been pointed out you shouldn't do this. As long as you control what's going on in the eval, the risk of security isn't a problem. BUT -- there's most likely a solution that makes more sense if you think deeply enough about it. I thought I had the perfect use case for dynamically creating new classes. Careful examination of the problem proved otherwise.
One potential solution -- use the stdClass for creating objects that are just data containers and don't need any methods.
Also -- as mentioned, you could use a script to manually generate lots of classes. In the case of classes mirroring your database tables, you could use the same logic I have above and instead of doing an eval, write that info to a file.
I think using eval() it's not a reliable solution, especially if your script or software will be distributed to different clients. Shared hosting providers always disable eval() function.
I'm thinking of a better aproach like this :
<?php
function __autoload( $class ) {
require 'classes/'.$class.'.php';
}
$class = 'App';
$code = "<?php class $class {
public function run() {
echo '$class<br>';
}
".'
public function __call($name,$args) {
$args=implode(",",$args);
echo "$name ($args)<br>";
}
}';
file_put_contents('classes/App.php' ,$code);
$a = new $class();
$a->run();
After finishing executing the code, you can delete the file if you want, I tested it and it works perfectly.
Using eval() is really a bad idea. It opens a large security hole. Just don't use it!
function __autoload($class) {
$code = "class $class {`
public function run() {
echo '$class<br>';
}
".'
public function __call($name,$args) {
$args=implode(",",$args);
echo "$name ($args)<br>";
}
}';
eval($code);
}
$app=new Klasse();
$app->run();
$app->HelloWorld();
This might help to create a class at runtime.
It also creates a methor run and a catchall method for unknown methods
But better create Objects at runtime, not classes.
This is almost certainly a bad idea.
I think your time would be better spent creating a script that would create your class definitions for you, and not trying to do it at runtime.
Something with a command-line signature like:
./generate_classes_from_db <host> <database> [tables] [output dir]
Please read everyone else answers on how this is truly a very very bad idea.
Once you understand that, here is a small demo on how you could, but should not, do this.
<?php
$clname = "TestClass";
eval("class $clname{}; \$cls = new $clname();");
var_dump($cls);
I have created a package that dynamically creates classes/interfaces/traits... and stores them into a file
you can then just include the created file to be able to use your class
package : https://packagist.org/packages/kanel/enuma
We can create class instance dynamically by following way
I also face this issue in Laravel 5.8 version and now it is working fine for me.
Give full path instead of the class Name
class TestController extends Controller
{
protected $className;
public function __construct()
{
$this->className = 'User';
}
public function makeDynamicInstance()
{
$classNameWithPath = 'App\\' . $this->className;
$classInstance = new $classNameWithPath;
$data = $classInstance::select('id','email')->get();
return $data;
}
}
Output
Illuminate\Database\Eloquent\Collection Object
(
[items:protected] => Array
(
[0] => App\User Object
(
[fillable:protected] => Array
(
[0] => name
[1] => email
[2] => password
[3] => user_group_id
[4] => username
[5] => facebook_page_id
[6] => first_name
[7] => last_name
[8] => email_verified
[9] => active
[10] => mobile
[11] => user_type
[12] => alternate_password
[13] => salt
[14] => email_verification_token
[15] => parent_id
)
[hidden:protected] => Array
(
[0] => password
[1] => remember_token
)
[casts:protected] => Array
(
[email_verified_at] => datetime
)
[connection:protected] => mysql
[table:protected] => users
[primaryKey:protected] => id
[keyType:protected] => int
[incrementing] => 1
[with:protected] => Array
(
)
[withCount:protected] => Array
(
)
[perPage:protected] => 15
[exists] => 1
[wasRecentlyCreated] =>
[attributes:protected] => Array
(
[id] => 1
[email] => admin#admin.com
)
[original:protected] => Array
(
[id] => 1
[email] => admin#admin.com
)
[changes:protected] => Array
(
)
[dates:protected] => Array
(
)
[dateFormat:protected] =>
[appends:protected] => Array
(
)
[dispatchesEvents:protected] => Array
(
)
[observables:protected] => Array
(
)
[relations:protected] => Array
(
)
[touches:protected] => Array
(
)
[timestamps] => 1
[visible:protected] => Array
(
)
[guarded:protected] => Array
(
[0] => *
)
[rememberTokenName:protected] => remember_token
)
)