Можно ли включить код в класс PHP?
Я хочу создать класс PHP, скажем Myclass.php. Теперь внутри этого класса я хочу определить только сам класс и некоторые переменные экземпляра. Но все методы должны поступать из файла Myclass_methods.php. Могу ли я просто включить этот файл в тело класса?
У меня есть веские причины, почему я хочу отделить это. Короче говоря, у меня будет бэкэнд, в котором я могу изменить бизнес-логику класса, в то время как все остальное должно оставаться нетронутым. Система поддерживает все ORM и другие вещи для меня.
Но если это плохая идея, было бы лучше перегенерировать весь файл класса после редактирования бизнес-логики (поэтому в этом случае будут определены пользовательские методы).
Вопрос о производительности: если во время одного запроса Myclass.php включен только один раз, на самом деле Myclass_methods.php также должен быть включен только один раз. Может быть, неправильно. Эксперты?
Ответы
Ответ 1
Нет. Вы не можете включать файлы в тело класса.
В файле, определяющем класс, вы можете включать только те файлы тела или вне тела класса.
Из вашего описания я хочу, чтобы вы этого хотели:
<?php // MyClass.php
class MyClass
{
protected $_prop;
include 'myclass-methods.php';
}
<?php // myclass-methods.php
public function myMethod()
{
$this->$_prop = 1;
}
Запуск этого кода приведет к
Parse error: syntax error, unexpected T_INCLUDE, expecting T_FUNCTION
Что возможно, но это
<?php // MyClass.php
class MyClass
{
protected $_prop;
public function __construct() // or any other method
{
include 'some-functions.php';
foo($b); // echoes 'a';
}
}
<?php // some-functions.php
$b = 'a';
function foo($str)
{
echo $str;
}
Выполняя это, импортирует содержимое include файла в область метода, а не в область класса. Вы можете включать функции и переменные во включенный файл, но не методы. Вы могли, но не должны вставлять в него все скрипты и изменять способ, например,
<?php // MyClass.php
// ...
public function __construct($someCondition)
{
// No No Code here
include ($someCondition === 'whatever') ? 'whatever.php' : 'default.php';
}
// ...
<?php // whatever.php
echo 'whatever';
<?php // default.php
echo 'foo';
Однако исправление класса таким образом, чтобы проявлять другое поведение, - это не то, как вы должны это делать в ООП. Это просто неправильно, и вы должны очищать глаза.
Поскольку вы хотите динамически изменять поведение, расширение класса также не является хорошим вариантом (см. ниже почему). То, что вы действительно хотите сделать, это написать interface и сделать ваш класс использующим объекты, реализующие этот интерфейс, таким образом, чтобы убедиться, что соответствующие методы доступны, Это называется Strategy Pattern и работает следующим образом:
<?php // Meowing.php
interface Meowing
{
public function meow();
}
Теперь у вас есть контракт, согласно которому все действия по подражанию должны подчиняться, а именно, иметь метод мяука. Затем определите поведение мяуканья:
<?php // RegularMeow.php
class RegularMeow implements Meowing
{
public function meow()
{
return 'meow';
}
}
Теперь, чтобы использовать его, используйте:
<?php // Cat.php
class Cat
{
protected $_meowing;
public function setMeowing(Meowing $meowing)
{
$this->_meowing = $meowing;
}
public function meow()
{
$this->_meowing->meow()
}
}
Добавив в SetMeowing Meowing TypeHint, вы убедитесь, что переданный параметр реализует интерфейс Meowing. Пусть определит другое поведение по методу:
<?php // LolkatMeow.php
class LolkatMeow implements Meowing
{
public function meow()
{
return 'lolz xD';
}
}
Теперь вы можете легко изменить поведение следующим образом:
<?php
require_once 'Meowing.php';
require_once 'RegularMeow.php';
require_once 'LolkatMeow.php';
require_once 'Cat.php';
$cat = new Cat;
$cat->setMeowing(new RegularMeow);
echo $cat->meow; // outputs 'meow';
// now to change the behavior
$cat->setMeowing(new LolkatMeow);
echo $cat->meow; // outputs 'lolz xD';
Хотя вы также могли бы решить вышеприведенный inheritance, указав abstract метод BaseCat и meow, а затем выводит из него конкретные классы RegularCat и Lolkat, вам нужно подумать о том, чего вы хотите достичь. Если ваши кошки никогда не изменят то, как они мяукают, продолжайте использовать наследование, но если ваш RegularCat и Lolkat должны иметь возможность делать произвольные мяки, используйте шаблон стратегии.
Для более шаблонов проектирования в PHP проверьте эти ресурсы:
Ответ 2
Невозможно создать основной класс с соответствующими базовыми функциональными возможностями, а затем расширить его с помощью необходимых методов - это похоже на более логичный подход.
Ответ 3
Начну с того, что не совсем понятно, почему эта проблема не решена наилучшим образом с использованием базового класса, содержащего методы, подклассы, содержащие данные, и динамическую загрузку классов. Я предполагаю, что у вас есть веская причина.
Как только ваш провайдер поддерживает PHP 5.4, вы можете делать то, что хотите, используя черты.
Файл кода:
if ($pet === 'dog') include 'dog.php';
elseif ($pet === 'cat') include 'cat.php';
else die('Unknown pet');
class Pet {
use PetSounds;
}
$myPet = new Pet();
$myPet->speak();
Файл cat.php
trait PetSounds {
function speak() { echo 'meow'; }
}
Файл dog.php
trait PetSounds {
function speak() { echo 'woof'; }
}
Вы можете сделать это еще более чистым, назвав оба файла include одинаковыми, поместив их в разные подкаталоги и используя set_include_path() или определяя функцию __autoload(), чтобы выбрать между ними. Как я уже сказал, эта же проблема может быть решена лучше, используя наследование. Если у вас проблема с множественным наследованием, хотя, например, у вас есть четыре вида домашних животных с пятью видами цветов с тремя типами волос, и вам нужно различное сочетание методов для каждого из 60 разных классов, это правильное решение.
5.4 в настоящее время является только кандидатом на выпуск (по состоянию на 2/24/2012) и даже когда-то выпущенный большинство хостов не будет поддерживать его в течение многих месяцев - мой ушел через 18 месяцев после выпуска 5.3, прежде чем они его поддержали. До тех пор вы должны писать полностью отдельные и полные файлы классов. Тем не менее, вы можете отформатировать свои классы с возможным изменением значений.
Прямо сейчас вы можете частично получить то, что хотите, используя магические методы и легко обновить до тех пор, пока они доступны.
Файл кода:
if ($pet === 'dog') include 'dog.php';
elseif ($pet === 'cat') include 'cat.php';
else die('Unknown pet');
class Pet {
public function __call($name, array $arguments)
{
array_unshift($arguments, $this);
return call_user_func_array("TraitFunc_$name", $arguments);
}
}
$myPet = new Pet();
$myPet->speak();
Файл cat.php
function TraitFunc_speak(Pet $that) { echo 'meow'; }
Файл dog.php
function TraitFunc_speak(Pet $that) { echo 'woof'; }
Однако вы ограничены тем, что ваши функции не могут получить доступ к приватным и защищенным свойствам и методам класса, и вы не можете использовать этот метод для предоставления магических методов, таких как __get(). Черты будут решать оба этих ограничения.
Ответ 4
Как насчет использования черт для этого? Будет ли это приемлемым вариантом? Это то, с чем я в настоящее время экспериментирую, и, похоже, работает довольно долго.
Упрощенная версия того, что я делаю, в основном такая. У меня есть приложение с общими файлами ядра и несколькими проектами. В рамках этих проектов у меня есть модули. Я хочу иметь функции, доступные для всего проекта на базовом уровне, но только для этого конкретного проекта.
Мой контроллер проекта
if(is_file(PROJECT_PATH.'/project_extensions.trait.php')){
// additional functions for this specific project
require_once(PROJECT_PATH.'/project_extensions.trait.php');
}else{
// no additional functions
trait Extensions{};
}
Class Project{
USE Extensions;
// default functions shared between all projects
function shared_stuff(){
}
}
Файл расширений
trait Extensions{
// project-specific extensions
function this_project_only(){
echo 'Project Only';
}
}
Файл модуля в проекте
class MyModule extends Modules{ // modules extends projects in a different class not relevant here
function do_something(){
echo $this->project_only();
}
}
Ответ 5
С PHP5.4 вы можете создавать динамические объекты следующим образом: https://github.com/ptrofimov/jslikeobject
Но это вряд ли лучшая практика.
Ответ 6
Возрождение старого вопроса, но это довольно простое решение. Вам нужны, чтобы общие вызовы функций были эксклюзивными для вашего класса? Если нет, просто укажите свой общий файл функции в той же области, что и ваш класс. Вам нужно будет создавать методы в своем классе, но им нужно будет только вызвать общую функцию. Вот простой пример сервера SOAP:
<?php
include 'post_function.php';
$server = new SoapServer( null, array('uri' => "http://localhost/") );
$server->setClass( 'postsoapclass' );
$server->handle();
class postsoapclass
{
public function animalNoise( $animal )
{
return get_animal_noise($animal);
}
}
?>
post_function.php
<?php
function get_animal_noise($animal)
{
if(strtolower(trim($animal)) == 'pig')
{
return 'Oink';
}
else
{
return 'This animal is mute';
}
}
?>
Ответ 7
Мне приходилось делать то, что вы описываете, в тех случаях, когда я поддерживаю бесплатную версию и премиальную версию того же программного обеспечения. Потому что, как заметил @Gordon, вы не можете сделать именно это:
class SomeClass {
premium_file = "premium.php";
if (file_exists($premium_file)) {
require($premium_file);
}
Вместо этого я делаю это:
premium_file = "premium.php";
if (file_exists($premium_file)) {
require($premium_file);
}
class SomeClass {
...
Для функций, которые вы хотите ссылаться, создайте методы класса в основном классе и вызовите включенный метод файла, передав указатель $this
в качестве параметра. Чтобы я мог сразу понять, где находятся функции, я буду префикс имени включенных функций, как показано ниже:
class SomeClass {
...
// Premium functions
public function showlist() {
premium_showlist($this);
}
Ответ 8
ВОЗМОЖНО!
(используя trait
с php 5.4)
your_file.php
include "additional_file.php";
class YourClass{
use additional_methods;
public function My1() {}
public function My2() {}
}
дополнительный_файл .php:
trait additional_methods{
public function My3() {}
public function My4() {}
}
(спасибо @user для ответа)