Можно ли включить код в класс PHP? - программирование
Подтвердить что ты не робот

Можно ли включить код в класс PHP?

Я хочу создать класс PHP, скажем Myclass.php. Теперь внутри этого класса я хочу определить только сам класс и некоторые переменные экземпляра. Но все методы должны поступать из файла Myclass_methods.php. Могу ли я просто включить этот файл в тело класса?

У меня есть веские причины, почему я хочу отделить это. Короче говоря, у меня будет бэкэнд, в котором я могу изменить бизнес-логику класса, в то время как все остальное должно оставаться нетронутым. Система поддерживает все ORM и другие вещи для меня.

Но если это плохая идея, было бы лучше перегенерировать весь файл класса после редактирования бизнес-логики (поэтому в этом случае будут определены пользовательские методы).

Вопрос о производительности: если во время одного запроса Myclass.php включен только один раз, на самом деле Myclass_methods.php также должен быть включен только один раз. Может быть, неправильно. Эксперты?

4b9b3361

Ответ 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 для ответа)