Вызов функции child-класса из другого child-класса в PHP5.
Пишу сайт, построенный на классах. Хочу разделить класс на отдельные файлы, содержащие подклассы, которые должны будут отвечать за свои личные действия. Но ума не приложу, можно ли вызвать из parent-класса функцию, находящуюся в child-классе? Либо-же вызвать функцию находящуюся в child-классе из другого child-класса, объединенного одним parent-классом?
Приведу пример, где собранно всё в один файл для удобства, который собственно и нужно починить.
class Main{
var $Template;
function Set_Template($id){
$this->Template=$id;
}
}
class Main_Page extends Main{
function Create_Main_Page(){
if(!($text=$this->Load_Template("template/".$this->Template."/Main.tpl",$array_params)))
return false;
else
return $text;
}
}
class Main_Template extends Main{
function Load_Template($tpl,$params){
$tplfile=file($tpl);
if(!is_array($tplfile))
return false;
for($i=0;$i<=count($tplfile);$i++){
$text.=$tplfile[$i]; // not complete
}
return $text;
}
}
$main=new Main;
$main->Set_Template("default");
echo $main->Create_Main_Page();
?>
Можно вместо $main=new Main; использовать $main=new Main_Page; - однако это тоже не поможет, все-равно надо будет вызвать функцию Load_Template из другого класса, что именно и составляет суть вопроса...
class Main{
var $Template;
function Set_Template($id){
$this->Template=$id;
}
}
class Main_Page extends Main{
function Create_Main_Page(){
if(!($text=Main_Template::Load_Template("template/{$this->Template}/Main.tpl",$array_params)))
return false;
else
return $text;
}
}
class Main_Template extends Main{
function Load_Template($tpl,$params){
$tplfile=file($tpl);
if(!is_array($tplfile))
return false;
for($i=0;$i<=count($tplfile);$i++){
$text.=$tplfile[$i]; // not complete
}
return $text;
}
}
$main=new Main_Page;
$main->Set_Template("default");
echo $main->Create_Main_Page();
?>
Измененные участки кода по сравнению с первым скриптом:
if(!($text=Main_Template::Load_Template("template/{$this->Template}/Main.tpl",$array_params)))
и
$main=new Main_Page;
Если есть какие-то альтернативы моему решению, постите, я для этого собственно тему и создал.
>>в котором класс был абсолютно нераздельным, в котором была большая куча функционала: админка, информер, регистрация, авторизация, проверка сессии...
Ты выделяешь существительные. Как правило это и есть классы. Класс != файл с функциями. Класс это есть представление сущности в жизни (например класс человек).
Теперь по поводу наследования. Наследование должно быть неким продолжением данной сущности, а не просто "мне нужен функционал этого класса, значит я его должен наследовать". Магнитола есть часть автомобиля, но она не должна наследовать класс автомобиля, а быть частью этого класса и наследовать класс "Музыка", т.к. магнитола есть ЧАСТЬ автомобиля, но она не явдяется автомобилем. От автомобиля можно наследовать "Автобус", "Грузовик" и т.д. Если ты реально хочешь в этом разобраться, то читай. Не одна книга по РНР не раскрывает сути ООП, она лишь показывает инструмент (по крайней мере я прочитал их прилично, но не понял ничего из них... может я и туповат конечно). Если реально хочешь разобраться, то попробуй изучать паттерны и UML. Так же было бы здорово ознакомится с Java или Ruby. Можешь так же поискать мои посты на форуме по поводу ООП, я думаю, что из них ты тоже сможешь что-нить подчерпнуть.
Можно отлично знать, что такое ООП (полиморфизм, там, наследование, прочее), но при этом писать крайне неудачные классы. Знание ООП это знание инструментария, что мы может получить от языка. Правильное разбиение на классы - хорошее понимание логики проектирования.
Так что, имхо, стоит читать не то, что такое ООП (автор это может и так пректасно знать), а именно ознакомиться с проектированием.
В твоем случае - наследование это такая схема:
class Processor{/* глобальные свойства и характеристики процессоров*/}
class Amd extends Processor{/* свойства и характеристики процессора Amd*/}
class Pentium extends Processor{/* свойства и характеристики процессора Pentium*/}
?>
В моём же случае, наследование определяет члены (части) главного класса. Например:
class Human{/* глобальные свойства и характеристика человека*/}
class Leg extends Human{/* свойства и характеристика ноги человека*/}
class Hand extends Human{/* свойства и характиристика руки человека*/}
?>
Смешно, правда. :) И даже непонятно, какой-же из наших вариантов правильный... А ведь оба они по-своему логичны. :)
class Human
{
/**
* @var Leg
*/
privat $leg
}
А класс человек должен наследовать класс Ребёнок (Рабочий и т.д.).
Посидел я тут денек, подумал так... и вроде начал разбираться с ООП... Если не составит труда - полистай мой скрипт, и ответь, правильно ли я понял? Думается у вас опыта больше, может совет какой дадите.
Так пойдет? Вообще, я ничего сложного не задумывал в скрипте, прямо наоборот старался. :(
А что к примеру непонятно?
return false; // Возвращаем false в случае ошибки.
while($row=$res->fetch_assoc()){
$Result[$pos]=$row; // записываем результат в Result[].
$pos++; // Передвигаем позицию указателя массива на 1.
}
$res->close;
Никогда так не делай. Ты считаешь, что у тебя всего один запрос на страницу будет?
По поводу панели. Что значит класс "Левая панель"? Нет и не должно быть такого класса. Есть класс Template, который и должен отвечать за вывод. Так же можешь сделать класс "Block" (Action), который и будет отвечать за сборку одного блока.
При работе с БД никогда не возвращай false (в случае ошибки), а возвращай (или пиши в лог) саму ошибку.
Функции news нет и быть не может. Может быть класс News.
Для mysql и mysqli нужны разные классы.
На пока всё, детально не рассматривал, немного занят.
Эта функция посылает запрос и сохраняет результат в отдельный массив, затем освобождает результат. А массив идет по ретурну... Этот класс - своеобразный вспомогательный класс, его задача максимально автоматизировать работу с Mysql. Я вообще ничего плохого в этой функции не вижу. Я могу сделать сколько угодно запросов в БД с помощью этой функции. Если ты думаешь про разность INSERT, SELECT, DELETE, UPDATE, то это учтется в будущем. В любом случае с этим я столкнулся бы в будущем.
2. Если судить по твоим словам в этом случае класс Page тоже не должен быть, Page - часть страницы, так сказать контент сайта. Panel - панель, на которой будет меню навигации и авторизации, всё вместе почти и будет всем сайтом.
3. Конечно, в будущем у меня есть планы на счет лога (в Mysql), пока-что у меня всё на стадии изучения и тестирования.
4. Я тоже так думал, спасибо что поправил.
5. А если подумать логически, то использовать один класс лучше и легче, чем делать проверку какой класс использовать во всех местах, откуда идут запросы, подключения, и отключения, сам подумай сколько это места займет, а тут всё в несколько строчек.
А если в общем - спасибо за наводки и за замечания.
Не люблю за кого-то отвечать, но, ИМХО, shaelf имел ввиду немного другое.
Лично я, бегло просмотрев код класса mysql_class немного впал в ступор. Да, возможно, "использовать один класс лучше и легче", в дальнейшем даже расширив его функционал для работы с другими БД. Но неужели это необходимо делать "в лоб" через if-конструкции в одном физическом классе? Я бы, к примеру, создал один абстрактный базовый SQL-класс с декларацией необходимых методов и унаследовал от него SQL_MySQL, SQL_PostgreSQL и.т.д. Далее по месту создания экземпляра обьекта для работы с необходимой БД выбирал соответствующий класс.
В итоге получилась бы банальная "расширяемая прослойка", с единым (по возможности) для подмножества БД интерфейсом.
3 минуты спустя: [COLOR="Gray"](перечитал цитату, много думал... :) )[/COLOR]
А почему собственно "во всех местах, откуда идут запросы, подключения, и отключения"? В любом случае речь о выборе класса шла бы только при создании экземпляра (обьекта), но не при обращении к методам (если они однородны/одноименны для нескольких соотв. классов) :confused:
1. Ты считаешь правильно после каждого запроса закрывать соеденение с БД?
2. Левая панель, правая, подвал... Да собсно положить, что это такое, их объеденяет одно - это блок странички. Чем для объекта отличается левая панель от подвала? А если ты навигацию будешь сверху делать, то будешь переделывать её в "Верхняя панель"? Класс "Навигация" может быть, Панель - нет!
3. Проехали
4. Пожалуйста
5. Кто тебе сказал, что лучше? А если потом ты туда оракл засунешь и постгрес?
PS Главной особенностью ООП является простота поддержки и доработки (ИМХО), а у тебя не объектный подход, а сборище процедур/функций которые объеденены в файл. Если хочешь понять основы, то запомни одну вещь (а лучше почитай книгу "UML за 24 часа"), каждое существительное является либо классом либо свойством класса, каждый глагол - методом. В данном случае мы имеем 2 существительных - mysql и mysqli. Это разные библиотеки, значит и классы должны быть разные.
Я бы наверное так бы давно и сделал. Только вот, увы, скилла не хватает. Я сам люблю всё делать как можно более лёгким способом. Я уже много научным тыком перетыкал, пользуясь родным php мануалом, и если честно многие примеры в нём меня просто поражают... Особенно вся ветка о классах... Это каким гением надо быть чтобы понять что и как работает? Ни тебе объяснений, ни разбора примеров... Так и не мог понять, к примеру, для чего нужны интерфейсы, абстрактные классы, и многие магические методы.
Очень бы хотел почитать подробнее по классам, собсно классы моё наиболее слабое место, так как эта тема сравнительно недавно начала мною изучаться. Может посоветуйте какой-нибудь мануал, или сайт по классам?
PS: Разговор однако далеко ушёл из под сабжа... Модераторы, переименуйте тему, пожалуйста, на что-нибудь более подходящее.
1. ??!! В данном случае идёт "закрытие" объекта с результатом, коннект держится на $Mysql_Conn переменной класса и никуда не пропадает. В данном случае в конце запроса закрывается $res, которому присвоен результат запроса. Смотрим пример указанный в мануале:
if ($result = $mysqli->query("SELECT Name FROM City LIMIT 10")) {
printf("Select returned %d rows.\n", $result->num_rows);
/* free result set */
$result->close();
}
2. Мало что понятно, если честно из твоего объяснения. :)
Насчет постскриптума согласен. Я не силен в ООП, я ведь только месяц назад приступил к его изучению. :)
Только вот насчет последних строк я несогласен. Может я класс неправильно назвал? Это вообще задумывалось как "главный интерфейс управления базами данных", ты правильно в общем сказал, что заранее надо продумывать все ситуации, что надо даже принимать во внимание такие вещи как добавление новых баз данных. Но давай еще раз посмотрим в чем же проблема, если сделать классы раздельными, то в сайте будет много лишних проверок на то, какой же класс использовать, именно поэтому я юзаю один класс со всеми необходимыми проверками.
Реализация без объяснения, это всё-равно что первокласнику показать уравнение из 11 класса и сказать "решай". :)
Мазохизм одним словом...
ООП, АОП, процедурное, функциональное... Это лишь подходы к программированию. Они общие для всех и для этого существует отдельная литература.
2. Нету никакого "Главного интерфейса для управления базами данных", есть "База Данных".
PS Учи паттерны.
Лично у меня, понимание этого частично пришло с необходимостью использования.
[COLOR="gray"]НЕТ, я ни кого не призываю сидеть, сложа руки и ждать озарения свыше. Та самая необходимость является следствием практики написания кода и чтения различной литературы, вне рамок официальных мануалов к средствам разработки.[/COLOR]
А вот теперь я точно не засну.
Ну ПОЧЕМУ? В каких именно "местах сайта" будет то самое "МНОГО лишних проверок"?
Я, конечно, не отрицаю, можно наварить каши, когда при вызове каждого метода созданного экземпляра придется писать switch на 20 пунктов для каждой предполагаемой БД, но для этого, ИМХО, скилла надо куда поболее, чем для более-менее грамотной реализации :)
Мазохизм одним словом...
[COLOR="Gray"](начинается зуд в руках; не будите во мне мастера безсмысленных аналогий :-)[/COLOR]
Попробуй открыть учебник по какому-нибудь графическому редактору, например Фотошопу. Если найдешь там главу, помимо ознакомления читателя с инструментами среды, обьясняющую, как "стать великим художником" - с меня виртуальное пиффко.
Мануал лишь приводит к сведенью де-факто существующие функции, конструкции, описывает синтаксис языка; он не в состоянии вдохновить на создание грамотно структурированной иерархии классов, научить "правильно" использовать ту или иную концепцию, подход в программировании, это, можно сказать, уже из области искусства, и тут, как уже отмечал shaelf, на помощь придут совершенно другие авторы и их "более художественные" [COLOR="gray"](в сравнении с формальным изложением мануалов, разумеется)[/COLOR] произведения.
Я бы наверное так бы давно и сделал. Только вот, увы, скилла не хватает. Я сам люблю всё делать как можно более лёгким способом. Я уже много научным тыком перетыкал, пользуясь родным php мануалом, и если честно многие примеры в нём меня просто поражают...
Рекомендую почитать:
PHP 5 - объектно-ориентированный язык
Правда её почему то сейчас убрали с сайта, может временные трудности. Если что прилагаю в аппаче MHT копию.
Вот это уже более-менее понятнее вроде как. Благодарю. :)
InterWen:
Ну прямо таки-обругали. Придётся поднапрячься поискать литературу, статейки по вопросу использования ООП. А то ваши объяснения слабо перевариваются моим мозгом. :)
PS В UML тоже всё в картинках)))))
А в картинках там все только в графическом представлении конкретных диаграм. )
Опять же по UML есть хорошие книжки опять же от создателей типа Буча. )
PS Если честно, то начал Буча (когда начинал) - не осилил :)
PSS У каждой крепости есть несколько ходов через энное место, если их найти, то можно получить тот-же результат, но проще... Каждому своё)
Раньше я использовал классы как "удобный пакет с функциями", что было также неудобно использовать, как и обычным процедурным путём, сейчас уже понимаю, что сделав грамотную картину из объектов, продумать их взаимоотношение - то получится много более качественный результат, например модульная система, которая будет несложно дорабатываться сторонними разработчиками, да и сам я буду меньше путаться в коде и находить ошибки.
Мануалы с картинками мне не надо. Я не тупой вроде, просто как и всем остальным новичкам здесь - мне просто нужен пинок в нужном направлении с некоторыми пояснениями, если что-то непонятно. :)
Перейду собсно к вопросу:
Начал изучать полиморфизм и абстрактные классы. Но немного непонятно, если честно... Вроде вижу плюсы (удобочитаемость, легкое расширение, в абстрактном классе объявленны все функции, с комментариями, в результате не нужно в каждом подклассе морочится с комментами к каждому методу), но... Вопрос: как вызвать нужный объект, исходя из предпочтения пользователя? Допустим в конфиге будет переменная $dbclient='mysql', как мне основываясь на этой переменной вызвать нужный подкласс DbClient'а? Делать выборку через case чтоли? Хорошо бы получить подсказку куда надо копать. :)
Вот мой пример скрипта, для удобства привёл только один экземпляр класса DbClient:
$dbclient="mysql";
$db["DbHost"]="127.0.0.1";
$db["DbUser"]="root";
$db["DbPass"]="";
$db["DbBase"]="test";
abstract class DbClient{
protected $DbConn;
protected $DbHost;
protected $DbUser;
protected $DbPass;
protected $DbBase;
function __construct($db){
if(!is_array($db)){
exit();
}
else{
$this->DbHost=$db["DbHost"];
$this->DbUser=$db["DbUser"];
$this->DbPass=$db["DbPass"];
$this->DbBase=$db["DbBase"];
}
}
/* Метод Connect, отвечает за соединение с базой. */
abstract public function Connect();
/* Метод Query, отвечает за посылку запроса в базу и запись результата в отдельный массив. */
abstract public function Query($query);
/* Метод Close, отвечает за закрытие подключения с базой. */
abstract public function Close();
function __destruct(){}
}
class MysqlClient extends DbClient{
public function Connect(){
if(!($this->DbConn=mysql_connect($this->DbHost,$this->DbUser,$this->DbPass))){
exit();
}
else{
if(!mysql_select_db($this->DbBase,$this->DbConn)){
exit();
}
}
return true;
}
public function Query($query){
$pos=0;
if(!($res=mysql_query($query,$this->DbConn)))
exit();
while($row=mysql_fetch_assoc($res)){
$Result[$pos]=$row;
$pos++;
}
mysql_free_result($res);
return $Result;
}
public function Close(){
mysql_close($this->Mysql_Conn);
}
}
function UseDbClient($object){
if($object instanceof DbClient){
// Операции с объектом?
}
else{
exit();
}
}
// Это понятно, напрямую вызывается нужный объект, а как вызвать
// предпочитаемый объект в зависимости от $dbclient?
$DbClient=new MysqlClient($db);
UseDbClient($DbClient);
?>
Или переменная
include("class.".$class.".php");
// файл class.Mysql.php
$DbClient=new MysqlClient($db);
но я бы через case делал - понятней будет
и ещё одно:
$dbclient="mysql";
$db["DbHost"]="127.0.0.1";
$db["DbUser"]="root";
$db["DbPass"]="";
$db["DbBase"]="test";
abstract class DbClient{
function __construct($db){
if(!is_array($db)){
exit();
}
else{
$this->DbHost=$db["DbHost"];
$this->DbUser=$db["DbUser"];
$this->DbPass=$db["DbPass"];
$this->DbBase=$db["DbBase"];
}
}
я бы не советовал так делать. Неудобно. Ведь допустим тогда надо помнить, какие ключи в массиве должны быть. Надо делать класс максимально понятным, и имена Базы Данных, пароль рута и т.д. передавать отдельно в класс. Хотя дело твое, но я считаю такую запись неудобной
и ещё:
exit();
}
сделай нормальное отладочное сообщение, чтоб потом понимал о чем ошибка. А то я помню, когда начинал - так же понаставил die(); везде, а потом полчаса рылся по каждому методу в попытках найти ошибку=)
Так же рекомендую почитать про try {} catch()
interface IDataBase
{
public function connect(array $config);
public function query($sql);
}
class DB
{
public static function getDriver($driver, array $config)
{
$className = 'DB_' . ucfirst($driver);
$da = new $className($config);
if($da instanceof IDataBase) {
return $da;
} else {
throw new Exception('Драйвер должен реализовывать IDataBase');
}
}
}
class DB_Mysql implements IDataBase
{
public function connect($config)
{
//Подклюаемся к БД
}
public function query($sql)
{
//что-то делаем с запросом
}
}
//Использование
$dbo = DB::getDriver('mysql', $config);
$dbo->connect()
//И т.д.
И ещё. Абстрактный класс сделан не для того, чтобы потом комментарии не писать.
ЗЫ Про die забудь вообще. Это пережиток 4 версии. Лучше последуй совету UAS
1. Как я и думал... реализация только перечислением через case, ладно, покопаем, может и другие пользователи чем-то подсобят...
2. По моему мнению массив легче передавать в функцию через global, да и просто это такая привычка моя. Но если подумать, то в чем-то ты прав, для меня это может в порядке вещей, а как быть с другими программистами? Они то не знают моей чудовищной логики. :)
3. Отладочные сообщения, примитивные, с путем к методу (не бэктрейс) у меня есть. Для того чтобы они не резали глаза я их убрал перед тем как запостить код.
4. Надо почитать. Насколько я понимаю с помощью этого можно сделать бэктрейс к обнаруженной ошибке.
shaelf:
Огромнейшее спасибо за пример. Этот пример, насколько я понял, использует паттерн? Довольно понятный пример даже без комментариев. Красота. :)
Насчет абстрактного класса, его роль примерно такая-же как и в интерфейсе? Примерно улавливаю суть, но, блин, просто не могу сформулировать на примере.
Насчет die() ясно. Как я понял, мне нужно научится работать с эксепшенами и изучить try и catch.
ЗЫ: Спасибо что помогаете, такими темпами я очень скоро смогу овладеть полной базой по ООП. :)
Раньше я использовал классы как "удобный пакет с функциями", что было также неудобно использовать, как и обычным процедурным путём,
Собственно по этому поводу вспомнил высказывание: "Си программист даже на ОО языке пишет процедурно". Собственно твой пример как раз подтверждает это правило очень сильно.
Скажу даже более. Даже есть четко знать и понимать, как работает ООП и вроде писать в этом стиле, то еще не факт, что код получается объектным. Слишком много вещей относящихся к этой сфере понимаются только в процессе разработки.
Как говорить моя тренерша по АТ: "практикс, практикс..." :D
>>Насчет абстрактного класса, его роль примерно такая-же как и в интерфейсе?
Интерфейсы используются для описания чего-то, чему следует следовать (во ляпнул:)). Т.е. у тебя есть например класс log который записывает в файл. Ты можешь поступить с ним точно так же как и я с БД. Интерфейс же позволит точно гарантировать тебе, что они реализуют точно так же твой функционал (например там точно будет write(), который в зависимости от типа драйвера будет писать в ошибку в файл, бд, выводить на экран и т.д.) и ты можешь смело его применять везде. Название драйвера лучше всего вынести в конфиг и оттуда его забирать, так как в этом случае точно будешь знать, что его нужно поменять всего в одном месте. Что касается абстрактных... Фактически это те же интерфейсы, только они реализуют какой-то общий для все классов функционал... Например тебе нужно описать сотрудников фирмы. Там есть менеджеры, директора, уборщики. Все они люди и что-то делают одинаково и что-то делают своё. Ты можешь в классе Работник реализовать базовый функционал и скажем метод выполнитьРаботу. Все последующие классы должны его реализовать (этот метод), т.к. скажем ходят они одинаково, а вот работу выполнять должны разную.
Мануалы с картинками мне не надо. Я не тупой вроде, просто как и всем остальным новичкам здесь - мне просто нужен пинок в нужном направлении с некоторыми пояснениями, если что-то непонятно. :)
Ну если мануалы с картинками - это ты про того же Буча, то это ты зря. Это не какие то мануалы, а все же классика введения в ООП. А Буч один из старейших идеологов ООП и создателей UML.
А после осиливания рекомендовал бы почитать книжку Паттерны проектирования от банды четырех. Раз уже сейчас начинаешь видеть их красоту. )
Ты неправильно меня понял... просто вы так говорите, будто я ничего не пойму без картинок. Мало еще видео не предложили, слава богу. :)
Мануал Буча я обязательно поищу, заинтриговали.
shaelf:
И опять-же, спасибо за объяснение. :)
Ты неправильно меня понял... просто вы так говорите, будто я ничего не пойму без картинок. Мало еще видео не предложили, слава богу. :)
Ну я такого не говорил ) А картинки там в тему )
Ща короче интерфейсик пишу. Проблема в следующем. Раньше просто писал что-то, наподобии:
function B();
}
сейчас решил уточнить - написать типы, т.е. хочу увидеть такое:
protected function B();
abstract function C();
}
Вообщем вот проблема на конкретном примере:
public function setHost(string $host);
}
abstract class Main implements IBase {
final public function setHost($host) {
$this->host=$host;
}
}
в этом случае выдается ошибка: Fatal error: Declaration of Main::setHost() must be compatible with that of IBase::setHost(). Решается это элементарно добавлением string перед $host. Final на ошибку не влияет. Воот.
Хочется чего - чтобы в интерфейсе метод описывался как final public function setHost(string $host). Но так нельзя. И в интерфейсе может определяться только public-методы, что так же неудобно. И не указывать типы пережаваемых перменных в методе классе (ну это не обязательно).
Можно ли этого доиться? А то чет не догоню
Добавлено: всё. Разобрался) Не правильно понял смысл интерфейса