0

I want to do something like this:

<?php
function addScript($name, $url) {
   $scripts[$name] = $url;
}

function displayScripts() {
 foreach ($scripts as $name => $url) {
  echo '<!-- Script: '.$name.' -->';
  echo '<script src="'.$url.'"></script>'
 }
}

The trick is: When the User want to add a Script in their classes.php, they just call addScript('jQuery','http...');

When the page is generated, the function displayScripts(); will be called and will throw all Scripts in the head of the file.

For obvious reasons I normally would just pass the $scripts array, but I don't want the user to do that. Also how would displayScripts() know about the $scripts array anyway?

I want that the user only calls addScript('jQuery','http...'); and no logic from my code.

The Problem is, that the array $scripts should something like global, but as far as I know, you should avoid global $var.

Does anyone has an idea how to do this?

Haudegen
  • 498
  • 1
  • 4
  • 17

2 Answers2

2

This is a problem of variable scope, you could in theory set an array for your scripts in the global scope, which is then accessible by your other functions, and throughout your files:

<?php
// globally defined
$scripts = array();

function addScript($script) {
    global $scripts;
    $scripts[] = $script;
}
...

and so on. This will obviously pollute the global space and all kinds ovf variables will be available, effectively claiming those variable names from being used safely.

A better solution would be to use some type of Registry. ideally you would have some kind of container create and keep track of this registry. But for example's sake, let's just use a standard singleton pattern. see this SO question for more info

class Scripts
{
    // static instance tracker
    protected static $instance;

    // the registry for the scripts
    protected $scripts = array();

    // static function to fetch the instance
    public static function getInstance()
    {
        if (!self::$instance) self::$instance = new Scripts();
        return self::$instance;
    }

    // protected constructor, we dont want anybody to create an instance
    protected function __construct() {}

    public function addScript($script) {
        $this->scripts[] = $script;
        return $this;
    }

    public function getScripts() {
        return $this->scripts;
    }

    public function renderTags() {
        foreach ($this->scripts as $script) {
            // render the html <script> tags and return html
        }
    }
}

Now you have a safe registry where you can store your scripts, usable anywhere where you included the class:

// somewhere in the code
Scripts::getInstance()->addScript('jquery.js')->addScript('jquery.ui.js');

// in the view rendering stage:
echo Scripts::getInstance()->renderTags();

I would encourage you to read up on Design patterns, and singletons, because the latter does come with some problems in the long run. You can find more info in this SO question

Community
  • 1
  • 1
NDM
  • 6,731
  • 3
  • 39
  • 52
  • First: Thank you for your answer! Can you just help me with this: What does `getIntance()` do? And why do I have to call it like `Scripts::getInstance()->add...` and not like @Rocket Hazmat said, `$scripts->add...` (after creating $scripts = new...) Thank you! – Haudegen Sep 15 '15 at 15:10
  • 1
    in the other answer, you still have to deal with one instance in the global scope, by using a singleton, you avoid this by having a class which keeps track of its own, single instance. This is also why you need the `getInstance` method. [have a look here](http://www.phptherightway.com/pages/Design-Patterns.html) – NDM Sep 15 '15 at 15:13
  • I am getting this error: Fatal error: `Call to undefined method Scripts::getInstance() in /var/www/html/test/classes/scripts.php in line` – Haudegen Sep 15 '15 at 15:39
  • sorry, there was a typo in my function name... :) I've updated the answer – NDM Sep 17 '15 at 07:52
1

You could do this in a class, so the $scripts variable is available to every method:

<?php
class Scripts{
    public $scripts = array();

    function addScript($name, $url) {
        $this->scripts[$name] = $url;
    }

    function displayScripts() {
        foreach ($this->scripts as $name => $url) {
            echo '<!-- Script: '.$name.' -->';
            echo '<script src="'.$url.'"></script>'
        }
    }
}

Then you can use it like:

$sm = new Scripts;
$sm->addScript('jQuery', '...');

$sm->displayScripts();
gen_Eric
  • 223,194
  • 41
  • 299
  • 337
  • Thank your for your answer, that looks good! Is there any why, I can avoid the `$sm->` at the beginning? – Haudegen Sep 15 '15 at 15:11
  • In this example, the `$sm->` is needed because the methods are part of the class object. You can't just do `addScript(...)` if you're using a class. – gen_Eric Sep 15 '15 at 15:38