Статические атрибуты и наследование

slego

Новичок
Статические атрибуты и наследование

Итак, есть абстрактый класс считывания конфига. Все его методы и свойства - статические
PHP:
abstract class Config
{
	protected static $_data = array();

	abstract public static function parse();
	
	public static function getParameter($param_name)
	{
		echo isset(self::$_data[$param_name]) ? 
					self::$_data[$param_name]	:
					"yo, madafaka!<br>";
	}
}
Теперь пытаюсь унаследовать от него два других класса,
каждый из которых работает со своим файлом.

PHP:
class Foo extends Config
{
	public static function parse()
	{
		 //... parsing the config structure

		 self::$_data["foo"] = "something";
		 echo self::$_data["foo"]."<br>";				
	}
};

class Bar extends Config
{
	public static function parse()
	{
		// ... parsing the config structure

		 self::$_data["bar"] = "something else";
		 echo self::$_data["bar"]."<br>";			
	}
};
Запускаем все это
PHP:
Foo::parse();				//	something
Foo::getParameter("foo");	//	yo, madafaka!

Bar::parse();				//	something else
Bar::getParameter("bar");	//	yo, madafaka!
Теперь, собственно, проблема: как видно из результатов работы скрипта, если проверять заполненость массива $_data в методе parse(), то у нас все в порядке - массив заполнен. Но если использовать метод getParameter(), определенный в предке, то обращение идет к статическому массиву $_data класса-родителя. Почему так?
Понимаю, что скорее всего, это я чего-то намудрил...
Можно, конечно, в абстрактном классе только объявить метод getParameter(), а реализовать (одним и тем же способом) уже в каждом потомке отдельно... Но не кузяво как-то это :(
Подскажите, пожалуйста, как ПРАВИЛЬНО реализовать такую схему.
Большое спасибо.
 

Profic

just Profic (PHP5 BetaTeam)
In fact static method calls are resolved at compile time. When using an explicit class name the method is already identified completely and no inheritance rules apply. If the call is done by self then self is translated to the current class, that is the class the code belongs to. Here also no inheritance rules apply.
(c) [m]oop5.static[/m]

Использовать классы не как namespace-ы, а как типы данных для объектов. Если же хочется использовать статический доступ - реализуй синглтон.

PHP:
<?php
class СonfigFile {
	const defaultFile = 'config.ini';

	private $file = NULL;
	private $config = array();

	public function __construct() {}

	public function load($file = self::defaultFile) {
		$config = parse_ini_file($file, true);
		foreach ($config as $section => $values) {
			$this->config[$section] = array();
			foreach ($values as $key => $value) {
				$this->config[$section][$key] = $this->convertValue($value);
			}
		}
		$this->file = $file;
	}

	public function get($section, $vars = array(), $default = NULL) {
		$configSection = $this->config[$section];
		if (!empty($vars)) {
			$values = array();
			foreach ($vars as $name) {
				$values[$name] = isset($configSection[$name]) ? $configSection[$name] : $default;
			}
			return $values;
		} else {
			return $configSection;
		}
	}

	private function convertValue($value) {
		if (is_numeric($value)) return (int) $value;
		if (strtolower($value) == 'true' || strtolower($value) == 'on') return true;
		if (strtolower($value) == 'false' || strtolower($value) == 'off') return false;
		return $value;
	}
}
?>
Это кастрированный пример того, как я это делаю. Не хочу сказать что это 100% правильно и так нужно делать всем, но мне так удобно :)
 

slego

Новичок
Про convertValue() - хорошая идея, об этом я как-то еще не размышлял...
За статические переменные - так и думал :/

Но вопрос, несколько о другом.
Получается, для каждого отдельного класса конфига ты предлагаешь реализовывать ПОЛНОСТЬЮ свои собственные методы без всякого наследования?

Ну, вот, допустим, у тебя есть несколько конфигов. Логика парсинга и разбора у каждого своя. Но логика работы метода convertValue() - везде ведь одна и та же. Будем просто переписывать ее (функцию) из класса в класс?

-~{}~ 15.08.05 13:17:

все, понял :) ЧЕРЕЗ SINGLETON
 

Profic

just Profic (PHP5 BetaTeam)
Самый примитивный вариант, запихать convertValue() в базовый класс для всех потомков (например ConfigBase). Для меня же базовым классом является ConfigFile. И все конфиги парсятся parse_ini_file().
А потом имеется "фабрика синглтонов".
PHP:
<?php
require_once 'function.autoload.php'; // функция __autoload()

class Singleton {
	private static $instances = array();

	private function __construct() {}

	public static function get($class) {
		if (!isset(self::$instances[$class])) {
			$classSingleton = 'Singleton' . $class;
			if (!class_exists($classSingleton)) {
				throw new SingletonException(sprintf('Cannot load class "%s"', $classSingleton));
			}
			$classReflect = new ReflectionClass($classSingleton);
			if (!$classReflect->implementsInterface('SingletonInterface')) {
				throw new SingletonException(sprintf('Class "%s" doesn\'t implements SingletonInterface', $classSingleton));
			}
			self::$instances[$class] = call_user_func(array($classSingleton, 'siGetObject'));
		}
		return self::$instances[$class];
	}
}

class SingletonException extends Exception {}

// в отдельном файле
interface SingletonInterface {
	public static function siGetObject();
}

// тоже в отдельном файле
class SingletonConfigFile implements SingletonInterface {
	public static function siGetObject() {
		$obj = new configFile();
		$obj->load('ini/config.ini');
		return $obj;
	}
}

?>
Таким образом все работает как я хочу, и не нужно сильно напрягаться :)
 
Сверху