Piszę już od jakiegoś czasu framework pod grę przeglądarkową. Gierka ma być ogólnodostępna i mieć proste api pod programowanie modyfikacji ale nie o tym. Ostatecznie kod ma być dostępny dla wszystkich tj. ktoś chce sobie pograć wchodzi na stronę pobiera grę (ew. modyfikacje) i uruchamia na localhoście lub na swoim serwerze. Nie martwię się zbytnio optymalizację gdyż gra nie zakłada istnienia potężnego serwera dla kilku tysięcy graczy - klepię ją po godzinach i dla zdobycia umiejętności w php, js, css itd. Chciałbym w tym wątku zadawać pytania odnośnie samego frameworka roboczo nazwanego Azure.
Z założenia składa się on z trzech różnych typów modułów:
- Komponenty - które można śmiało wykorzystać także w innych aplikacjach np. konfiguracja, dla wyróżnienia posiadają przed swoimi nazwami C,
- Moduły bazowe - stanowią integralną część frameworka np. główna klasa aplikacji, initializer (coś jak bootstrap zend) itd. przed nazwą posiadają B,
- Narzędzia - proste moduły pozwalające na operowaniu danymi np. kontenery, stosy, validatory, filtry itd. literka U
I tak to zaprojektowałem, że każdy komponent dziedziczy ze wspólnej klasy abstrakcyjnej a wygląda ona tak:
namespace Azure\CBase\Abstracts
{
if (!defined('AZURE')) exit('No direct script access allowed');
use Azure\CBase\Exceptions\CInvalidArgumentException;
/*
* The base class for each component AzureFramework (class starts
* with the letter "C") shorting methods for basic operations on
* the component and its automatic configuration.
*
* Most contained in the base class methods and fields component has the
* prefix "c". In addition, the method can not be overridden because they
* are final, exception is the method of "__initialize".
*
* Classes that inherit from the base class component to work properly on
* an automatic configuration mechanism requires a protected field called
* an "cAccessorPrefixes" containing an array with the names of prefixes
* methods mutators.
*
* @package Azure\CBase
* @subpackage Abstracts
*/
class CBaseAbstract
{
/*
* It contains an associative array through which the component is
* automatically configured, for example, if the array is an element
* called "base" and the "example" component will try to find the field
* in the component class called "base" and assign it
* a value of "example".
*
* @access protected
* @var array
*/
protected $cConfiguration = array();
/*
* Contains an object of reflection class, required for proper operation
* of the class.
*
* @access protected
* @var \ReflectionClass|null
*/
protected $cReflection = null;
/*
* It contains an array with the names of properties defined
* in the class.
*
* @access protected
* @var array
*/
protected $cProperties = array();
/*
* It contains an array with the names of methods defined
* in the class.
*
* @access protected
* @var array
*/
protected $cMethods = array();
/*
* It includes an array of items that have not been used for automatic
* configuration object. Sometimes they are used to configure other
* objects contained in another object.
*
* @access protected
* @var array
*/
protected $cOptions = array();
/*
* Contains arguments that were passed to the constructor of the class
* beyond the configuration of object.
*
* @access protected
* @var array
*/
protected $cArguments = array();
/*
* The default constructor argument using the component configuration to
* automatically configure the object.
*
* @access public
* @param array $configuration
* @return void
*/
public function __construct($configuration = array())
{
$arguments = func_get_args();
unset($arguments[0]);
$arguments = array_values($arguments);
if (count($arguments) > 0) {
$this->cArguments = $arguments;
}
$this->cPrepare();
$this->cAutoConfigure($configuration);
$this->cInitialize();
}
/*
* Method of preparing an object for action, creates a reflection class
* and sets the basic values of certain fields.
*
* @access protected
* @return void
*/
final protected function cPrepare()
{
$reflection = new \ReflectionClass($this);
$this->cReflection = $reflection;
$properties = $reflection->getProperties();
$methods = $reflection->getMethods();
foreach ($properties as $property) {
array_push($this->cProperties, $property->getName());
}
foreach ($methods as $method) {
array_push($this->cMethods, $method->getName());
}
}
/*
* The basic method of the component responsible for the configuration
* based on the array passed to the constructor.
*
* @access protected
* @param array $configuration
* @throws Azure\CBase\Exceptions\CInvalidArgumentException If provided
* argument is not a type of 'array'.
* @return void
*/
final protected function cAutoConfigure($configuration)
{
if (is_array($configuration)) {
$config = $configuration;
} else {
throw new CInvalidArgumentException('Component configuration must be array or ' .
'object instance of Azure\\CConfiguration\\Interfaces\\CConfiguration.');
}
$this->cConfiguration = $config;
foreach ($config as $name => $value) {
$propertyName = lcfirst($name);
if (in_array($propertyName, $this->cProperties)) {
$method = $this->cSearchPropertyMethod($name);
if ($method !== false) {
$this->$method($value);
} else {
$this->cSetOption($name, $value);
}
} else {
$this->cSetOption($name, $value);
}
}
}
/*
* The method looks for the method name using the fields contained in
* the class 'cAccessorPrefixes'.
*
* @access protected
* @param string $name
* @return false|string
*/
final protected function cSearchPropertyMethod($name)
{
if (in_array('cAccessorPrefixes', $this->cProperties)) {
$prefixes = $this->cAccessorPrefixes;
} else {
$prefixes = array();
}
foreach ($prefixes as $prefix) {
$methodName = $prefix . ucfirst($name);
if (in_array($methodName, $this->cMethods)) {
return $methodName;
}
}
return false;
}
/*
* Sets the value of the option object is not used during the automatic
* configuration.
*
* @access protected
* @param string $name
* @param mixed $value
* @return void
*/
final protected function cSetOption($name, $value)
{
$this->cOptions[$name] = $value;
}
/*
* Gets the option to object if it does not exist returns false.
*
* @access protected
* @param string $name
* @return mixed If option doesn`t exists return false.
*/
final protected function cGetOption($name)
{
if ($this->cHasOption($name)) {
return $this->cOptions[$name];
}
return false;
}
/*
* The method checks whether the option with the given name exists in
* the component and returns a boolean value of this operation.
*
* @access protected
* @param string $name
* @return boolean
*/
final protected function cHasOption($name)
{
if (array_key_exists($name, $this->cOptions)) {
return true;
}
return false;
}
/*
* The method removes the option of the given name of the object if
* it exists.
*
* @access protected
* @param string $name
* @return void
*/
final protected function cDeleteOption($name)
{
if ($this->cHasOption($name)) {
unset($this->cOptions[$name]);
}
}
/*
* When an object is not required to perform an operation before the
* constructor (which in the case of a component is actually executed
* in the __ initialize). This method spradza whether there is a method
* __ before the class, if it starts it, then proceed just as in the
* case of the initialize method __ and __ after.
*
* @access protected
* @return void
*/
final protected function cInitialize()
{
if (in_array('__before', $this->cMethods)) {
$this->__before($this->cArguments);
}
if (in_array('__initialize', $this->cMethods)) {
$this->__initialize($this->cArguments);
}
if (in_array('__after', $this->cMethods)) {
$this->__after($this->cArguments);
}
}
/*
* Constructors in components do not work in the normal way. Similar
* action to the default constructor (__construct) has a method
* __initialize. The argument is passed the array of successive
* arguments forwarded her at the time of object creation (excluding the
* configuration of an object).
*
* @access protected
* @param array $arguments
* @return void
*/
protected function __initialize($arguments)
{
}
}
}
Dzięki dziedziczeniu z tej klasy każdy komponent może być konfigurowany przy jego utworzeniu.
class SimpleComponent extends Azure\CBase\Abstracts\CBaseAbstract
{
protected $name;
protected $surname;
protected $age;
public function setName($name)
{
$this->name = (string) $name;
}
public function setSurname($surname)
{
$this->surname = (string) $surname;
}
public function setAge($age)
{
$this->age = (int) $age;
}
}
Tak wygląda przykładowa klasa komponentu. I teraz:
$c = new SimpleComponent(array('age' => 26, 'name' => 'Jan', 'surname' => 'kowalski'));
Sprawi, że odpowiednie pola zostaną uzupełnione wartościami z tablicy przekazanej do konstruktora. Może się zdarzyć tak, że do konstruktora będziemy potrzebowali przekazać pewną inną wartość, możemy wtedy nadpisać konstruktor:
class SimpleComponent extends Azure\CBase\Abstracts\CBaseAbstract
{
protected $name;
protected $surname;
protected $age;
public function __construct($adress, $options = array())
{
parent::__construct($options, $adress);
}
public function setName($name)
{
$this->name = (string) $name;
}
public function setSurname($surname)
{
$this->surname = (string) $surname;
}
public function setAge($age)
{
$this->age = (int) $age;
}
}
W takim wypadku obsługę zmiennej $adress możemy napisać w chronionej metodzie __initialize() komponentu. Teraz jest napisane to w ten sposób, że wszystkie argumenty oprócz $options są przekazywane jako tablica do metody __initialize($arguments) i muszę je wyszukiwać według kolejności np. $arguments[0].
Moje pytanie jak zrobić to w ten sposób aby poszczególne argumenty z __construct() były przekazywane w taki sam sposób do metody __initialize() np.w __construct($options = array(), $path, $extension) aby __initializer otrzymał tablicę $arguments = array('path' => 'wartosc', 'extension' => 'wartosc')?