Home » Php » code generation – Dynamically generate classes at runtime in php?

code generation – Dynamically generate classes at runtime in php?

Posted by: admin April 23, 2020 Leave a comment

Questions:

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?

How to&Answers:

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]

Answer:

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
}

Answer:

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.

Answer:

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.

Answer:

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.

Answer:

Using eval() is really a bad idea. It opens a large security hole. Just don’t use it!

Answer:

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.

Answer:

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);

Answer:

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] => [email protected]
                        )

                    [original:protected] => Array
                        (
                            [id] => 1
                            [email] => [email protected]
                        )

                    [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
                )
)

Answer:

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