1

I am writing a program which processes geographical coordinates of a city and I want to randomly select my city in each run; the process for all cities are the same.

I have a PHP class for each city which contains geographical coordinates of it; for example:

<?php
namespace Utility\Locations;
class Tehran
{
    const MIN_LAT = 35.325;
    const MAX_LAT = 35.390;
    const MIN_LNG = 51.165;
    const MAX_LNG = 51.230;
}

In another PHP file I make use of the class as:

use Utility\Locations\Tehran;
use Utility\Locations\Karaj;
...
protected function MyProcessingMethod () {
    ...
    $city =  Faker::create()->randomElement(array("Tehran", "Karaj"));
    echo $city::MIN_LAT;
    ...
}
...

If it helps, the above mentioned file is a CodeCeption Cest and MyProcessingMethod is used as a DataProvider. When I run my tests using codecept run scenarios/MyCest.php, I get this error:

PHP Fatal error:  Uncaught Error: Class 'Tehran' not found in /home/zeinab/PhpstormProjects/test/scenarios/MyCest.php:190
Stack trace:
#0 [internal function]: MyCest->MyProcessingMethod()
#1 /home/zeinab/PhpstormProjects/test/vendor/codeception/codeception/src/Codeception/Util/ReflectionHelper.php(47): ReflectionMethod->invokeArgs(Object(MyCest), Array)
#2 /home/zeinab/PhpstormProjects/test/vendor/codeception/codeception/src/Codeception/Test/Loader/Cest.php(65): Codeception\Util\ReflectionHelper::invokePrivateMethod(Object(MyCest), Object(ReflectionMethod))
#3 /home/zeinab/PhpstormProjects/test/vendor/codeception/codeception/src/Codeception/Test/Loader.php(109): Codeception\Test\Loader\Cest->loadTests('/home/zeinab/Ph...')
#4 /home/zeinab/PhpstormPro in /home/zeinab/PhpstormProjects/test/scenarios/MyCest.php on line 190

I've read PHP official documentations on this type of usage, but all examples there were using the classes that are defined inside destination file.

I also tried this:

$city =  Faker::create()->randomElement(array(Tehran::class, Karaj::class));
echo $city::MIN_LAT;

But I got the same error.

Is there anyway to do what I want to?

EDIT 1: I have put the path to the classes in psr-4 tag of my composer.json:

"autoload": {
    "psr-4": {
        "Utility\\": "Utility/"
    }
}

It seems there is no problem in loading the classes, since using them directly works fine. For example the following code is doing great:

echo Tehran::MIN_LAT;
Zeinab Abbasimazar
  • 9,835
  • 23
  • 82
  • 131
  • where do you include those classes? I see `use ` - but no `require_once` before it - or PSR4 autoloader? Also, wouldn't it better to have one class called City and have a bunch of properties for that got via a db ID? – treyBake Jan 02 '19 at 09:06
  • you can see this question and answers [instantiate class with variables](https://stackoverflow.com/questions/534159/instantiate-a-class-from-a-variable-in-php) – Ali Ghalambaz Jan 02 '19 at 09:10
  • @treyBake, please review my edit. – Zeinab Abbasimazar Jan 02 '19 at 10:09

2 Answers2

0

add loader to first line of your code :

function loader($className)
{
    $fileName = str_replace('\\', '/', $className) . '.php';
    require_once $fileName;
}
spl_autoload_register('loader');

then use namespaces like directory structures :

Ahvaz.php in app/core/city directory

namespace app\core\city;
class Ahvaz
{
//...
}

when you load a class Ahvaz.php like this

$var = new Ahvaz();

you need to add use app\core\city;

then you have :

use app\core\city;
$var = new Ahvaz();

it will include Ahvaz.php from app/core/city directory

Ali Ghalambaz
  • 420
  • 3
  • 7
  • I don't want to put methods to return constants. BTW, this code won't work if I put my class in another file. My issue is that I want to use classes from other files. – Zeinab Abbasimazar Jan 02 '19 at 10:48
  • if you want access your classes from another files you can use __autoload function and(or) namespaces – Ali Ghalambaz Jan 02 '19 at 12:04
0

I created this code snippet for example purposes - so it may not fully reflect your methods but the answer is the principle :)

<?php
    # create our city classes
    class Brighton
    {
        const X = 1;
        const Y = 2;
    }

    class London
    {
        const X = 3;
        const Y = 4;
    }

    # create our get city class
    class Foo
    {
        public function getCity()
        {
            $possibleCities = ['Brighton', 'London', 'Birmingham']; # add a city that's not a class for testing
            $city = rand(0, count($possibleCities) -1);

            return $possibleCities[$city];
        }
    }

    # init foo
    $foo = new Foo();
    $city = $foo->getCity(); # get city

    # check if class exists, if it does, init it
    $cityClass = (class_exists($city) ? new $city() : null);

    # check if class is empty - if it isn't echo CONST X
    echo (!empty($cityClass) ? $cityClass::X : $city. ' class not found');

How to include the classes (if they're in separate files):

Imagine the tree is:

|-app
|-----core
|---------city
|-------------Brighton.php
|-------------London.php
|-----Foo.php

In Foo.php

<?php
    require_once 'city/Brighton.php';
    require_once 'city/London.php';

    use City\Brighton;
    use City\London;

    # etc. etc

And then Brighton.php and London.php have the namespace of City:

<?php
    namespace City;

    class Brighton
    {} # etc
treyBake
  • 6,440
  • 6
  • 26
  • 57