Могу ли я включить код в класс PHP? - PullRequest
54 голосов
/ 24 декабря 2009

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

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

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

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

Ответы [ 8 ]

163 голосов
/ 24 декабря 2009

Нет. Вы не можете включать файлы в тело класса.
В файл, определяющий класс, вы можете включать только файлы в теле метода или вне тела класса .

Из вашего описания я понимаю, что вы хотите:

<?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;
}

Делая это таким образом, вы импортируете содержимое включаемого файла в область действия метода, а не в область действия класса. Вы можете включать функции и переменные во включаемый файл, но не методы. Вы могли бы, но не должны включать в него также целые сценарии и изменять метод, например,

<?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';

Однако исправление класса таким образом, чтобы продемонстрировать другое поведение, совсем не то, как вы должны это делать в ООП. Это просто неправильно, и ваши глаза должны кровоточить.

Поскольку вы хотите динамически изменять поведение, расширение класса также не является хорошим вариантом (см. Ниже, почему). То, что вы действительно захотите сделать, это написать интерфейс и заставить ваш класс использовать объекты, реализующие этот интерфейс, таким образом гарантируя, что соответствующие методы доступны. Это называется шаблон стратегии и работает так:

<?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()
    }
}

Добавляя Meowing TypeHint в setMeowing, вы убедитесь, что переданный параметр реализует интерфейс 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';

Хотя вы также могли бы решить вышесказанное с помощью наследования , определив abstract BaseCat и метод meow, а затем извлекая из этого конкретные классы RegularCat и Lolkat, вы должны учитывать, что вы хочу добиться. Если ваши кошки никогда не изменят то, как они мяукают, продолжайте и используйте наследование, но если ваши RegularCat и Lolkat должны выполнять произвольные мяу, то используйте шаблон Стратегии.

Чтобы узнать больше шаблонов проектирования в PHP, проверьте следующие ресурсы:

8 голосов
/ 24 декабря 2009

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

6 голосов
/ 24 февраля 2012

Я начну с того, что мне не совсем понятно, почему эту проблему лучше всего не решить, используя базовый класс, содержащий методы, подклассы, содержащие данные, и динамическую загрузку классов. Я полагаю, у вас есть веская причина.

Как только ваш провайдер поддерживает 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'; }
}

Вы можете сделать это еще чище, назвав оба включаемых файла одинаковыми, поместив их в разные подкаталоги и используя set_include_path () или определив функцию __autoload () для выбора между ними. Как я уже сказал, эту же проблему можно решить лучше, используя наследование. Если у вас есть проблема с типом множественного наследования, если, например, у вас есть четыре вида домашних животных с пятью видами цветов с тремя типами волос, и вам требуется разная комбинация методов для каждого из 60 различных классов, это правильное решение .

5.4 в настоящее время является просто Кандидатом на выпуск (по состоянию на 24.02.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 (). Черты решат оба эти ограничения.

5 голосов
/ 08 сентября 2014

Как насчет использования черт для этого? Будет ли это приемлемым вариантом? Это то, над чем я сейчас экспериментирую, и, похоже, работает довольно долго.

Упрощенная версия того, что я делаю, в основном такова. У меня есть приложение с общими файлами ядра и несколькими проектами. В рамках этих проектов у меня есть модули. Я хочу иметь функции, которые доступны для всего проекта на уровне ядра, но только для этого конкретного проекта.

Контроллер моего проекта

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();
  }
}
0 голосов
/ 27 февраля 2019

Я недавно столкнулся с этим и нашел решение, которое помогло в моем случае. Я хотел, чтобы в классе было много функций, но класс стал раздутым, поэтому хотел разделить функции класса на группы для удобства чтения. Это заняло немного времени, но так как функции класса не полагались (сильно) на $ this, я удалил «$ this» из функций класса и создал несколько вспомогательных файлов для включения этих функций. Когда $ this было необходимо, я, тем не менее, мог переместить функцию в вспомогательный файл, передав $ this функции, добавляя публичные функции set / get, где это необходимо. Это взлом, но он обязательно кому-нибудь поможет

` класс myClass { var x;

    function myClass()
    {
        $this->x = 0;
    }

    function myFunc1Group1()
    {
        $x = $this->x;
        $x++;
        $this->x = $x;
    }
    function myFunc2Group1(){}

    function myFunc1Group2(){}
    function myFunc2Group2(){}
}

`

можно обойти до

` класс myClass { var x;

    function myClass()
    {
        $this->x = 0;
    }

    function doSomething()
    {
        // not called on $this but takes $this as a parameter
        myFunc1Group1($this);
    }
}

`

и набор вспомогательных функций 1

function myFunc1Group1($THIS_OBJECT) { $x = $THIS_OBJECT->getX(); $x++; $THIS_OBJECT->setX($x); } function myFunc2Group1($THIS_OBJECT){}

и набор вспомогательных функций 2 и т. Д.

Вероятно, не лучший маршрут во всех случаях, но мне очень помог. В основном функции класса должны были только конструировать и делегировать, а вычисления помещались в помощники.

0 голосов
/ 21 июня 2017

Мне приходилось делать то, что вы описываете в тех случаях, когда я поддерживаю бесплатную версию и премиум-версию того же программного обеспечения. Потому что, как заметил @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);
    }
0 голосов
/ 28 сентября 2015

Оживление старого вопроса, но это довольно простое решение. Вам нужны вызовы общих функций, чтобы быть эксклюзивными для вашего класса? Если нет, просто включите ваши общие функциональные файлы в ту же область, что и ваш класс. Вам нужно будет создать методы в вашем классе, но им нужно будет только вызвать общую функцию. Вот простой пример 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';
    }
}

?>
0 голосов
/ 09 января 2013

С версии PHP5.4 вы можете создавать динамические объекты , например: https://github.com/ptrofimov/jslikeobject

Но это едва ли лучшая практика.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...