Kirjautuminen

Haku

Tehtävät

Keskustelu: Nettisivujen teko: PHP: Autoloader ei löydä luokkaa

Sivun loppuun

AkeMake [18.11.2016 22:08:11]

#

Yritän käyttää sivuillani pChartia, mutta kun luon oliota siitä, niin sivu ilmoittaa, että "Class 'Chart\pData' not found". Käytän sivuilla seuraavaa autoloaderia:

<?php

namespace includes;

/**
 * An example of a general-purpose implementation that includes the optional
 * functionality of allowing multiple base directories for a single namespace
 * prefix.
 *
 * Given a foo-bar package of classes in the file system at the following
 * paths ...
 *
 *     /path/to/packages/foo-bar/
 *         src/
 *             Baz.php             # Foo\Bar\Baz
 *             Qux/
 *                 Quux.php        # Foo\Bar\Qux\Quux
 *         tests/
 *             BazTest.php         # Foo\Bar\BazTest
 *             Qux/
 *                 QuuxTest.php    # Foo\Bar\Qux\QuuxTest
 *
 * ... add the path to the class files for the \Foo\Bar\ namespace prefix
 * as follows:
 *
 *      <?php
 *      // instantiate the loader
 *      $loader = new \Example\Autoloader;
 *
 *      // register the autoloader
 *      $loader->register();
 *
 *      // register the base directories for the namespace prefix
 *      $loader->addNamespace('Foo\Bar', '/path/to/packages/foo-bar/src');
 *      $loader->addNamespace('Foo\Bar', '/path/to/packages/foo-bar/tests');
 *
 * The following line would cause the autoloader to attempt to load the
 * \Foo\Bar\Qux\Quux class from /path/to/packages/foo-bar/src/Qux/Quux.php:
 *
 *      <?php
 *      new \Foo\Bar\Qux\Quux;
 *
 * The following line would cause the autoloader to attempt to load the
 * \Foo\Bar\Qux\QuuxTest class from /path/to/packages/foo-bar/tests/Qux/QuuxTest.php:
 *
 *      <?php
 *      new \Foo\Bar\Qux\QuuxTest;
 *
 */
class Autoloader
{
    /**
     * An associative array where the key is a namespace prefix and the value
     * is an array of base directories for classes in that namespace.
     *
     * @var array
     */
    protected $prefixes = array();

    /**
     * Register loader with SPL autoloader stack.
     *
     * @return void
     */
    public function register()
    {
        spl_autoload_register(array($this, 'loadClass'));
    }

    /**
     * Adds a base directory for a namespace prefix.
     *
     * @param string $prefix The namespace prefix.
     * @param string $base_dir A base directory for class files in the
     * namespace.
     * @param bool $prepend If true, prepend the base directory to the stack
     * instead of appending it; this causes it to be searched first rather
     * than last.
     * @return void
     */
    public function addNamespace($prefix, $base_dir, $prepend = false)
    {
        // normalize namespace prefix
        $prefix = trim($prefix, '\\') . '\\';

        // normalize the base directory with a trailing separator
        $base_dir = rtrim($base_dir, DIRECTORY_SEPARATOR) . '/';

        // initialize the namespace prefix array
        if (isset($this->prefixes[$prefix]) === false) {
            $this->prefixes[$prefix] = array();
        }

        // retain the base directory for the namespace prefix
        if ($prepend) {
            array_unshift($this->prefixes[$prefix], $base_dir);
        } else {
            array_push($this->prefixes[$prefix], $base_dir);
        }
    }

    /**
     * Loads the class file for a given class name.
     *
     * @param string $class The fully-qualified class name.
     * @return mixed The mapped file name on success, or boolean false on
     * failure.
     */
    public function loadClass($class)
    {
        // the current namespace prefix
        $prefix = $class;

        // work backwards through the namespace names of the fully-qualified
        // class name to find a mapped file name
        while (false !== $pos = strrpos($prefix, '\\')) {

            // retain the trailing namespace separator in the prefix
            $prefix = substr($class, 0, $pos + 1);

            // the rest is the relative class name
            $relative_class = substr($class, $pos + 1);

            // try to load a mapped file for the prefix and relative class
            $mapped_file = $this->loadMappedFile($prefix, $relative_class);
            if ($mapped_file) {
                return $mapped_file;
            }

            // remove the trailing namespace separator for the next iteration
            // of strrpos()
            $prefix = rtrim($prefix, '\\');
        }

        // never found a mapped file
        return false;
    }

    /**
     * Load the mapped file for a namespace prefix and relative class.
     *
     * @param string $prefix The namespace prefix.
     * @param string $relative_class The relative class name.
     * @return mixed Boolean false if no mapped file can be loaded, or the
     * name of the mapped file that was loaded.
     */
    protected function loadMappedFile($prefix, $relative_class)
    {
        // are there any base directories for this namespace prefix?
        if (isset($this->prefixes[$prefix]) === false) {
            return false;
        }

        // look through base directories for this namespace prefix
        foreach ($this->prefixes[$prefix] as $base_dir) {

            // replace the namespace prefix with the base directory,
            // replace namespace separators with directory separators
            // in the relative class name, append with .php
            $file = $base_dir
                  . str_replace('\\', '/', $relative_class)
                  . '.php';

            // if the mapped file exists, require it
            if ($this->requireFile($file)) {
                // yes, we're done
                return $file;
            } else if ($this->requireFile($base_dir . str_replace('\\', '/', $relative_class) . '.class.php')) {
                // yes, we're done with .class ending in the file name
                return $base_dir . str_replace('\\', '/', $relative_class) . '.class.php';
            }
        }

        // never found it
        return false;
    }

    /**
     * If a file exists, require it from the file system.
     *
     * @param string $file The file to require.
     * @return bool True if the file exists, false if not.
     */
    protected function requireFile($file)
    {
        if (file_exists($file)) {
            require $file;
            return true;
        }
        return false;
    }
}

Olen kopioinut tuon suoraan jostain netin syövereistä ja tähän asti se on toiminut hyvin. Lisäsin siihen kohdan, joka tunnistaa myös .class.php päätteiset tiedostot, koska tämän haettavan tiedoston polku on muotoa polku/libraries/pChart2.1.4/class/pData.class.php. Otan autoloaderin sivuilla käyttöön koodilla

// Autoloader
$loader = new includes\Autoloader;
// register the autoloader
$loader->register();
$loader->addNamespace('Chart', PATH_LIBRARIES . '/pChart2.1.4/class');

ja luokka, jossa haluan pData oliota käyttää sisältää tällaista

namespace components\graphs;

/* pChart library inclusions */
use Chart\pData;

class Helper {


/*
 *ja myöhemmin erään funktion sisällä on seuraava rivi, josta tuo herjakin "Class 'Chart\pData' not found" tulee.
 */
$MyData = new pData();
// Kokeilin myös seuraavaa ja tästä tuli ilmoitus "Class 'pData' not found".
$MyData = new \pData();
// Ja tästä tuli ilmoitus "Class 'components\graphs\Chart\pData' not found".
$MyData = new Chart\pData();

Olen koettanut kaikenlaista, mutta kun ei ole niin hyvin perillä tästä olio-maailmasta, niin ei ole käsitystä mitä tässä tapahtuu. Onkohan ongelma siinä, että autoloader ei jostain syystä tunnista tuota addNamespacea tai että toimitaan väärässä nimiavaruudessa... Vaikuttaisi kuitenkin siltä, että autoloader hakee pData luokan sisältävän tiedoston ihan oikein, koska kun sisällytin sen manuaalisesti include komennolla, niin sain herjoja, joiden mukaan luokkaa ei voi esitellä uudestaan. Mistähän tässä nyt kenkä puristaa?

The Alchemist [19.11.2016 10:09:35]

#

Luokan nimi ei ole Chart\pData vaan pData ilman mitään namespacea. Autoloaderisi ei osaa ladata luokkia, jotka eivät ole tunnetun namespacen sisällä. Nykyaikana ei ole oikein mitään järkeä käyttää juosten kustuja toteutuksia, koska autoloaderillekin on jo pari standardia php-maailmassa. Sitä paitsi Pchart näyttää olevan melkolailla kuollut projekti, koska sitä ei ole päivitetty kolmeen vuoteen. Kannattaa etsiä ajantasaisempia työkaluja.

AkeMake [19.11.2016 13:44:45]

#

Kerrot nyt minulle vain mikä se ongelma on, mutta et anna siihen ratkaisua.
Toisaalta minulla on kyllä eräs toinen luokka, jossa olen määritellyt näin

namespace components\interesting;
use libraries\Stats;

Ja tässä tapauksessa, vaikka ollaan namespacen components/interesting sisällä, niin autoloader osaa ihan oikein hakea luokan tuolta sijainnista libraries/stats.php.

Juosten kustu autoloader, vai? Joskus aikoinaan vähän luin tästä PHP:n autoloaderista, mutten alkanut sen syvällisemmin siihen tutustua. Jostain päin nettiä sattui sitten silmiin tällainen toteutus, joka näytti sopivan yksinkertaiselta, jotta pystyin sitä ymmärtää ja käyttää. Jos on, jokin helppo ratkaisu tähän tilalle, joka ei vaadi minulta paljon työtä tai perehtymistä autoloaderin sielunmaailmaan, niin saa ehdottaa sellaista. Mitenkä olisiko paikallaan lisätä uusi osio PHP-oppaaseeen tähän autoloaderiin liittyen?

Kävin ehkä parikymmentä sivustoa läpi, jotka tarjosivat tätä kuvaajien esittämistä PHP:llä ja kaikki muut ilmaiset toteutukset olivat vielä huomattavasti tuota pChartia vanhempia. Minusta näyttää siltä, ettei nykyään ole enää tällaista ihan tuoretta ilmaista kuvaajien esittämiseen tarkoitettua pakettia tarjolla. Jos sellaisen löydät, niin saa toki vinkata asiasta.

Metabolix [19.11.2016 14:37:11]

#

Mielestäni The Alchemistin viesti oli täysin selvä: pChartin luokat eivät ole missään namespacessa. Koodissasi lukee ”use Chart\pData”, mutta ei ole olemassa luokkaa Chart\pData, koska pChartin luokat eivät ole missään namespacessa (ellet ole itse muokannut koodia). Luokan nimi on siis pelkästään ”\pData”.

Jos tämä ei vielä ollut tarpeeksi rautalangasta väännetty, niin vaihda use-riviksi ”use \pData” tai poista koko use-rivi ja käytä koodissa suoraan luokkaa nimellä ”\pData”.

Toinen kysymys onkin sitten, miten asettelet tiedostot niin, että autoloader löytää ne.

pChartin käyttö ei ole tässä vaiheessa järkevää, koska sitä ei päivitetä ja se voi pian lakata toimimasta, kuten seuraava PHP:n varoitus kertoo: ”Deprecated: Methods with the same name as their class will not be constructors in a future version of PHP; pData has a deprecated constructor in ...”

Nettisivuille kuvaajia voi luoda JavaScriptilla (esim. Google Charts tai Raphaël). Tällöin PHP:llä tulostetaan vain data jossain järkevässä muodossa ja JavaScriptilla tehdään siitä kuvaaja.

AkeMake [21.11.2016 20:59:57]

#

No oikeastaan se kysymys johon haluaisin vastausta on juurikin tuo, että mitä minun pitäisi tehdä, että autoloader löytäisi pData:n. Ehkä muotoilin kysymyksen sitten alunperin jotenkin huonosti. En mieluusti lähtisi muuttamaan tiedostorakennetta, joten miten muuten tämän saisi toimimaan? Onnistuisiko se (tai olisiko järkevää) esimerkiksi muuttamalla tämä pData luokka staattiseksi?

Tämä pData saa nyt luvan kelvata, koska teen tätä vain omaan käyttöön enkä laita edes nettiin. Tämä tulee pyörimään ainoastaan oman MAMP:ni päällä ja jos tuo pData jossain vaiheessa feilaa, niin ei haittaa.

Metabolix [21.11.2016 21:11:15]

#

Nopealla vilkaisulla näyttäisi, että voisit vain lisätä pChartin polun autoloaderiin ilman mitään namespacea:

$loader->addNamespace('', PATH_LIBRARIES . '/pChart2.1.4/class');

Eikö sinun pitänyt ymmärtää tuon autoloaderin toimintaa itsekin?

Jos autoloaderille kerrotaan vain, että Chart-nimiavaruus löytyy tietystä paikasta, ”yllättäen” se ei yritä ladata sieltä luokkaa \pData ilman nimiavaruutta. Ja toisaalta, jos koodissa lukee Chart\pData, ei paljon auta, vaikka autoloader vahingossa lataisi tavallaan oikean \pData-luokan.

Jos haluat pChartin muualle kuin globaaliin nimiavaruuteen, voit tietysti yrittää laittaa kaikkiin pChartin tiedostoihin alkuun haluamasi namespace-määreen ja toivoa, että ne vielä toimivat.

AkeMake kirjoitti:

Onnistuisiko se (tai olisiko järkevää) esimerkiksi muuttamalla tämä pData luokka staattiseksi?

Miten ajattelit staattisuuden vaikuttavan asiaan? (Ja eihän PHP:ssä ole mitään staattisia luokkia, on vain staattisia metodeja tavallisilla luokilla.)

AkeMake [21.11.2016 21:34:42]

#

Eipä tuo polun lisääminen autoloaderiin näytä toimivan vaan edelleen sanoo, ettei pData luokkaa löydy. Niin minä luulin ymmärtäväni tuon autoloaderin toimintaa, mutta näköjään sitten en kuitenkaan.

Selitin näköjään tuosta staattisuudesta ihan omiani. Tarkoitin siis, että tekisin pData luokkaan staattisen funktion, jossa luon pDatasta olion ja palautan sen. Tuo pChart vain käyttää useita olioita, joten minun pitäisi tehdä tämä viritelmä useamman kerran useisiin luokkiin. Se tieten toimisi, mutta olisiko järkevä ratkaisu?

The Alchemist [21.11.2016 21:54:08]

#

No ei tietenkään ole. Eihän se edes ratkaise ongelmaasi, koska et voi käyttää luokkaa, josset saa ladattua sitä! Mikäli et osaa konffata autoloaderia ja haluat väistämättä käyttää vanhentunutta paskaa, niin sitten voit includettaa tiedoston koodiisi ihan käsipelillä.

AkeMake [21.11.2016 22:34:17]

#

Okei, sain nyt luettua luokan, mutta heitti niin paljon virhettä ja varoitusta, etten viitsi lähteä niitä purkamaan. Näköjään tuo on jo nyt liian vanhaa tavaraa, että sitä voisi kunnolla käyttää. Pakko siis alkaa oikeasti tutustumaan tuohon Google Chartsiin.


Sivun alkuun

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta