워드 프레스 테마의 functions.php 파일에서 코드를 구성 하시겠습니까?


92

WordPress에 대한 사용자 정의가 많을수록이 파일을 구성하거나 분할 해야하는지에 대해 더 많이 생각하기 시작합니다.

더 구체적으로, 관리 영역에만 적용되는 사용자 정의 기능과 공용 웹 사이트에만 적용되는 다른 사용자 정의 기능이있는 경우 모든 관리 기능을 자체 파일에 포함하거나 함께 그룹화 해야하는 이유가 있습니까?

파일을 별도의 파일로 나누거나 그룹화하면 WordPress 웹 사이트 속도가 빨라지거나 WordPress / PHP가 is_admin 코드 접두사가있는 기능을 자동으로 건너 뛰나요?

큰 함수 파일을 처리하는 가장 좋은 방법은 무엇입니까 (광산은 1370 줄입니다).

답변:


120

테마의 코드 functions.php가 압도되기 시작 하는 시점에 도달하면 코드 를 여러 파일로 나눌 준비가 된 것입니다. 나는이 시점에서 거의 두 번째 본성으로하는 경향이 있습니다.

를 사용하여 테마의 포함 파일 functions.php파일

내 테마 디렉토리 아래에 "includes" 라는 서브 디렉토리를 작성 하고 당시의 말에 따라 구성되는 포함 파일로 코드를 세그먼트 화한다 (이는 사이트가 발전함에 따라 지속적으로 코드를 리팩터링하고 이동시키는 것을 의미한다). 실제 코드를 넣습니다 functions.php. 모든 것은 포함 파일에 들어갑니다. 단지 내 취향.

예를 들어 여기 WordPress Answers의 질문에 대한 답변을 테스트하는 데 사용하는 테스트 설치가 있습니다. 질문에 대답 할 때마다 다시 필요할 때를 대비하여 코드를 보관하십시오. 이것은 실제 사이트에서 수행 할 작업이 아니지만 코드를 나누는 메커니즘을 보여줍니다.

<?php 
/*
 * functions.php
 * 
 */
require_once( __DIR__ . '/includes/null-meta-compare.php');
require_once( __DIR__ . '/includes/older-examples.php');
require_once( __DIR__ . '/includes/wp-admin-menu-classes.php');
require_once( __DIR__ . '/includes/admin-menu-function-examples.php');

// WA: Adding a Taxonomy Filter to Admin List for a Custom Post Type?
// http://wordpress.stackexchange.com/questions/578/
require_once( __DIR__ . '/includes/cpt-filtering-in-admin.php'); 
require_once( __DIR__ . '/includes/category-fields.php');
require_once( __DIR__ . '/includes/post-list-shortcode.php');
require_once( __DIR__ . '/includes/car-type-urls.php');
require_once( __DIR__ . '/includes/buffer-all.php');
require_once( __DIR__ . '/includes/get-page-selector.php');

// http://wordpress.stackexchange.com/questions/907/
require_once( __DIR__ . '/includes/top-5-posts-per-category.php'); 

// http://wordpress.stackexchange.com/questions/951/
require_once( __DIR__ . '/includes/alternate-category-metabox.php');  

// http://lists.automattic.com/pipermail/wp-hackers/2010-August/034384.html
require_once( __DIR__ . '/includes/remove-status.php');  

// http://wordpress.stackexchange.com/questions/1027/removing-the-your-backup-folder-might-be-visible-to-the-public-message-generate
require_once( __DIR__ . '/includes/301-redirects.php');  

또는 플러그인 만들기

다른 옵션은 기능별로 코드를 그룹화하고 자신의 플러그인을 만드는 것입니다. 나를 위해 테마 functions.php파일 에서 코딩을 시작 하고 코드가 완성 될 때까지 대부분의 코드를 플러그인으로 옮겼습니다.

그러나 PHP 코드 구성으로부터 큰 성능 향상은 없습니다

반면에 PHP 파일을 구성하는 것은 순서 및 유지 관리 성을 만드는 데 99 %, 성능에 대해 1 %입니다 ( HTTP를 통해 브라우저에서 호출하는 조직 .js.css파일은 완전히 다른 경우이며 성능에 큰 영향을 미칩니다). 서버의 PHP 코드는 성능 측면에서 중요하지 않습니다.

그리고 코드 조직은 개인 취향입니다

마지막으로 코드 구성은 개인 취향입니다. 어떤 사람들은 코드를 구성하는 방식이 싫은 것처럼 코드를 구성하는 방식을 싫어합니다. 마음에 드는 것을 찾아서 고수하지만 시간이 지남에 따라 더 많이 배우고 더 편하게 전략을 발전시킬 수 있습니다.


좋은 대답, 방금 함수 파일을 분할 해야하는 시점에 도착했습니다. 언제 frunctions.php에서 플러그인으로 이동하는 것이 편리하다고 생각하십니까? 당신은 당신의 대답에서 말했다 : 나는 코드가 완성 될 때까지 대부분의 코드를 플러그인으로 옮겼다 . 나는 그것을 완전히 이해하지 못한다.
Saif Bechan

5
"플러그인 생성"의 경우 +1 보다 구체적으로, " 기능 플러그인 "
Ian Dunn

3
상대 경로를 사용하는 것이 모든 종류의 설정에서 신뢰할 수있는 것은 아니지만 절대 경로를 항상 대신 사용해야합니다.
Mark Kaplun

2
@MarkKaplun-당신은 절대적으로 정확합니다. 이 답변을 썼기 때문에 그 교훈을 어렵게 배웠습니다. 답변을 업데이트하겠습니다. 이것을 지적 해 주셔서 감사합니다.
MikeSchinkel

"정의되지 않은 상수 DIR 사용 -C : \ wamp \ www \ site \ wp-content \ themes \ mytheme \ functions.php에서 ' DIR '으로 가정 함) -PHP v5.6.25 및 PHP v7.0.10-수 없습니다 주석에서이 DIR의 형식을 올바르게 지정하지만 (underscoreunderscoreDIRunderscoreunderscore), dirname (underscoreunderscoreFILEunderscoreunderscore)
Marko

50

늦은 답변

올바른 방법으로 파일을 포함시키는 방법 :

function wpse1403_bootstrap()
{
    // Here we load from our includes directory
    // This considers parent and child themes as well    
    locate_template( array( 'inc/foo.class.php' ), true, true );
}
add_action( 'after_setup_theme', 'wpse1403_bootstrap' );

플러그인에서도 마찬가지입니다.

올바른 경로 또는 URi를 얻는 방법

또한 다음과 같은 파일 시스템 API 함수를 살펴보십시오.

  • home_url()
  • plugin_dir_url()
  • plugin_dir_path()
  • admin_url()
  • get_template_directory()
  • get_template_directory_uri()
  • get_stylesheet_directory()
  • get_stylesheet_directory_uri()
  • 기타

의 수를 줄이는 방법 include/require

디렉토리에서 모든 파일 을 가져와야 하는 경우

foreach ( glob( 'path/to/folder/*.php' ) as $file )
    include $file;

이는 실패 (프로덕션 사용에 적합) /로드 할 수없는 파일을 무시합니다.

이 동작을 변경하려면 개발 중에 다른 구성을 사용하려고 할 수 있습니다.

$files = ( defined( 'WP_DEBUG' ) AND WP_DEBUG )
    ? glob( 'path/to/folder/*.php', GLOB_ERR )
    : glob( 'path/to/folder/*.php' )

foreach ( $files as $file )
    include $file;

편집 : OOP / SPL 접근

방금 돌아와서이 답변이 점점 더 많은지지를 받고 있음을 보았을 때, 나는 PHP 5.3+ 세계에서 요즘 어떻게하고 있는지 보여줄 수 있다고 생각했습니다. 다음 예제는이라는 테마 하위 폴더에서 모든 파일을로드합니다 src/. 여기에는 메뉴, 이미지 등과 같은 특정 작업을 처리하는 라이브러리가 있습니다. 모든 단일 파일이로드 될 때 이름을 신경 쓰지 않아도됩니다. 이 디렉토리에 다른 하위 폴더가 있으면 무시됩니다.

\FilesystemIterator는 PHP 5.3+입니다 supercedor 오버 \DirectoryIterator. 둘 다 PHP SPL의 일부입니다. PHP 5.2에서는 내장 SPL 확장 기능을 끌 수 있었지만 (모든 설치 중 1 % 미만이 그랬습니다) SPL은 이제 PHP 핵심의 일부입니다.

<?php

namespace Theme;

$files = new \FilesystemIterator( __DIR__.'/src', \FilesystemIterator::SKIP_DOTS );
foreach ( $files as $file )
{
    /** @noinspection PhpIncludeInspection */
    ! $files->isDir() and include $files->getRealPath();
}

이전에는 여전히 PHP 5.2.x를 지원했지만 다음과 같은 솔루션을 사용 \FilterIterator했습니다. src/Filters디렉토리의 A 는 폴더의 점 포인터가 아닌 파일 만 검색하고 a \DirectoryIterator는 반복 및로드를 수행합니다.

namespace Theme;

use Theme\Filters\IncludesFilter;

$files = new IncludesFilter( new \DirectoryIterator( __DIR__.'/src' ) );
foreach ( $files as $file )
{
    include_once $files->current()->getRealPath();
}

\FilterIterator만큼 간단했다 :

<?php

namespace Theme\Filters;

class IncludesFilter extends \FilterIterator
{
    public function accept()
    {
        return
            ! $this->current()->isDot()
            and $this->current()->isFile()
            and $this->current()->isReadable();
    }
}

현재까지는 PHP 5.2가 죽고 / EOL이 아니라 (그리고 5.3도), 더 많은 코드와 게임에 파일이 하나 더 있다는 사실이 있기 때문에 나중에 나아갈 필요가 없으며 PHP 5.2.x를 지원할 이유가 없습니다.

요약

더 자세한 내용은 WPKrauts에서 확인할 수 있습니다 .

편집 분명히 올바른 방법은 네임 스페이스를 통해 이미 정의 된 적절한 디렉토리에 모든 것을 넣어 PSR-4 자동로드를 namespace위해 준비된 d 코드 를 사용 하는 것입니다. 그런 다음 Composer 와 a 를 사용하여 종속성을 관리하고 PHP 자동 로더를 자동으로 빌드하십시오 (이를 호출하여 파일을 자동으로 가져옵니다 ). WP Starter 는 PHP 세계에서 사실상의 표준이며, 가장 쉬운 방법이며 사전 자동화되고 단순화되었습니다 .composer.jsonuse \<namespace>\ClassName


5

보일러 플레이트에서 사용자 정의 함수를 사용하여 테마 디렉토리에서 함수라는 폴더를 찾으십시오 (없는 경우). 그런 다음 해당 폴더에서 찾은 모든 .php 파일의 배열을 만들고 (있는 경우) include ()를 실행합니다. 그들 각각에.

이렇게하면 새로운 기능을 작성해야 할 때마다 함수 폴더에 PHP 파일을 추가하기 만하면 사이트에 코드를 코딩 할 필요가 없습니다.

<?php
/* 
FUNCTIONS for automatically including php documents from the functions folder.
*/
//if running on php4, make a scandir functions
if (!function_exists('scandir')) {
  function scandir($directory, $sorting_order = 0) {
    $dh = opendir($directory);
    while (false !== ($filename = readdir($dh))) {
      $files[] = $filename;
    }
    if ($sorting_order == 0) {
      sort($files);
    } else {
      rsort($files);
    }
    return ($files);
  }
}
/*
* this function returns the path to the funtions folder.
* If the folder does not exist, it creates it.
*/
function get_function_directory_extension($template_url = FALSE) {
  //get template url if not passed
  if (!$template_url)$template_url = get_bloginfo('template_directory');


  //replace slashes with dashes for explode
  $template_url_no_slash = str_replace('/', '.', $template_url);

  //create array from URL
  $template_url_array = explode('.', $template_url_no_slash);

  //--splice array

  //Calculate offset(we only need the last three levels)
  //We need to do this to get the proper directory, not the one passed by the server, as scandir doesn't work when aliases get involved.
  $offset = count($template_url_array) - 3;

  //splice array, only keeping back to the root WP install folder (where wp-config.php lives, where the front end runs from)
  $template_url_array = array_splice($template_url_array, $offset, 3);
  //put back togther as string
  $template_url_return_string = implode('/', $template_url_array);
  fb::log($template_url_return_string, 'Template'); //firephp

  //creates current working directory with template extention and functions directory    
  //if admin, change out of admin folder before storing working dir, then change back again.
  if (is_admin()) {
    $admin_directory = getcwd();
    chdir("..");
    $current_working_directory = getcwd();
    chdir($admin_directory);
  } else {
    $current_working_directory = getcwd();
  }
  fb::log($current_working_directory, 'Directory'); //firephp

  //alternate method is chdir method doesn't work on your server (some windows servers might not like it)
  //if (is_admin()) $current_working_directory = str_replace('/wp-admin','',$current_working_directory);

  $function_folder = $current_working_directory . '/' . $template_url_return_string . '/functions';


  if (!is_dir($function_folder)) mkdir($function_folder); //make folder, if it doesn't already exist (lazy, but useful....ish)
  //return path
  return $function_folder;

}

//removed array elements that do not have extension .php
function only_php_files($scan_dir_list = false) {
  if (!$scan_dir_list || !is_array($scan_dir_list)) return false; //if element not given, or not array, return out of function.
  foreach ($scan_dir_list as $key => $value) {
    if (!strpos($value, '.php')) {

      unset($scan_dir_list[$key]);
    }
  }
  return $scan_dir_list;
}
//runs the functions to create function folder, select it,
//scan it, filter only PHP docs then include them in functions

add_action('wp_head', fetch_php_docs_from_functions_folder(), 1);
function fetch_php_docs_from_functions_folder() {

  //get function directory
  $functions_dir = get_function_directory_extension();
  //scan directory, and strip non-php docs
  $all_php_docs = only_php_files(scandir($functions_dir));

  //include php docs
  if (is_array($all_php_docs)) {
    foreach ($all_php_docs as $include) {
      include($functions_dir . '/' . $include);
    }
  }

}

5
@mildfuzz : 멋진 트릭. 사이트를 시작할 때 한 번에 쉽게 할 수있는 모든 페이지로드에 대해 프로덕션 코드에 개인적으로 사용하지 않습니다. 또한 밑줄로 시작하는 것을로드하지 않는 등 파일을 생략하는 방법을 추가하여 테마 디렉토리에 진행중인 작업을 계속 저장할 수 있습니다. 그렇지 않으면 좋습니다!
MikeSchinkel

아이디어를 좋아하지만 각 요청에 대해 불필요한로드가 발생할 수 있음에 동의합니다. 새로운 파일이 추가되거나 특정 시간 간격으로 업데이트되는 경우 최종 functions.php 파일을 생성하는 간단한 방법이 자동으로 어떤 유형의 업데이트로 캐시되는지 아는 아이디어가 있습니까?
NetConstructor.com

멋지지만 유연성이 떨어지고 공격자가 코드를 삭제하면 어떻게됩니까? 포함 순서가 중요한 경우 어떻게해야합니까?
Tom J Nowell

1
@ MikeSchinkel 방금 작업 파일 foo._php를 호출 한 다음 실행하려는 경우 _php를 삭제하십시오.
Mild Fuzz

@NetConstructor : 일부 솔루션에도 관심이 있습니다.
카이저

5

폴더 안의 파일에 기능을 사용하고 싶습니다. 이 방법을 사용하면 새 파일을 추가 할 때 새로운 기능을 쉽게 추가 할 수 있습니다. 그러나 나는 항상 클래스 또는 네임 스페이스로 작성합니다-함수, 메소드 등의 네임 스페이스에 대해 더 많은 제어권을 부여하십시오.

작은 예 아래; 또한 클래스 * .php에 대한 계약과 함께 사용

public function __construct() {

    $this->load_classes();
}

/**
 * Returns array of features, also
 * Scans the plugins subfolder "/classes"
 *
 * @since   0.1
 * @return  void
 */
protected function load_classes() {

    // load all files with the pattern class-*.php from the directory classes
    foreach( glob( dirname( __FILE__ ) . '/classes/class-*.php' ) as $class )
        require_once $class;

}

테마에서 나는 종종 다른 시나리오를 사용합니다. 지원 ID에서 externel 파일의 기능을 정의합니다 (예 참조). externel 파일의 기능을 쉽게 비활성화 할 경우 유용합니다. WP 핵심 기능을 사용하고 require_if_theme_supports()지원 ID가 활성화 된 경우에만로드합니다. 다음 예에서는 파일을로드하기 전에 줄 에서이 지원되는 ID를 정의했습니다.

    /**
     * Add support for Theme Customizer
     * 
     * @since  09/06/2012
     */
    add_theme_support( 'documentation_customizer', array( 'all' ) );
    // Include the theme customizer for options of theme options, if theme supported
    require_if_theme_supports( 
        'documentation_customizer',
        get_template_directory() . '/inc/theme-customize.php'
    );

이 테마저장소 에서 더 많은 것을 볼 수 있습니다 .


4

네트워크 설치를 통해 약 50 개의 고유 한 사용자 정의 페이지 유형이있는 서버를 다른 언어로 사이트를 관리합니다. 플러그인 톤과 함께.

우리는 어느 시점에서 그것을 모두 분할해야했습니다. 20-30k 줄의 코드가있는 함수 파일은 전혀 재미 있지 않습니다.

우리는 코드베이스를 더 잘 관리하기 위해 모든 코드를 리팩토링하기로 결정했습니다. 기본 워드 프레스 테마 구조는 작은 사이트에는 적합하지만 더 큰 사이트에는 적합하지 않습니다.

우리의 새로운 functions.php에는 사이트를 시작하는 데 필요한 것만 포함되어 있지만 특정 페이지에 속하는 것은 없습니다.

이제 우리가 사용하는 테마 레이아웃은 MCV 디자인 패턴과 비슷하지만 절차 적 코딩 스타일입니다.

예를 들어 회원 페이지 :

page-member.php . 페이지를 초기화합니다. 올바른 아약스 함수 또는 유사한 호출. MCV 스타일의 컨트롤러 부품과 동일 할 수 있습니다.

functions-member.php . 이 페이지와 관련된 모든 기능을 포함합니다. 이것은 회원을위한 기능이 필요한 다른 서버 페이지에도 포함되어 있습니다.

content-member.php . HTML에 대한 데이터를 MCV의 모델과 동등하게 준비합니다.

layout-member.php . HTML 부분.

Efter는 이러한 변경 작업을 수행하여 개발 시간이 50 % 쉽게 단축되었으며 이제 제품 소유자가 새로운 작업을 수행하는 데 어려움을 겪고 있습니다. :)


7
이를보다 유용하게 만들기 위해이 MVC 패턴이 실제로 어떻게 작동하는지 보여줄 수 있습니다.
kaiser

나는 또한 당신의 접근법의 예를 보는 것이 좋으며, 바람직하게는 세부 사항 / 다양한 상황이 있습니다. 이 접근 방식은 매우 잘 들립니다. 서버로드 / 성능을 다른 사람들이 사용하는 표준 방법과 비교 했습니까? 가능한 경우 github 예제를 제공하십시오.
NetConstructor.com

3

자식 테마 functions.php 파일에서 :

    require_once( get_stylesheet_directory() . '/inc/custom.php' );

0

functions.php에서 필요한 파일을 호출하는 더 우아한 방법은 다음과 같습니다.

require_once locate_template ( '/ inc / functions / shortcodes.php');


4
locate_template()세 번째 매개 변수가 있습니다 ...
fuxia

0

@kaiser@mikeschinkel 의 답변을 결합 했습니다 .

/includes폴더의 테마에 대한 모든 사용자 정의가 있으며 해당 폴더 내에 모든 것이 하위 폴더로 나뉩니다.

나는 단지 /includes/admin그 내용과 그 하위 내용을 포함 하고 싶을 때만true === is_admin()

폴더 iterator_check_traversal_callback를 반환하여 제외 false하면 해당 하위 디렉토리는 반복되지 않습니다 (또는로 전달됨 iterator_check_traversal_callback).

/**
 *  Require all customizations under /includes
 */
$includes_import_root = 
    new \RecursiveDirectoryIterator( __DIR__ . '/includes', \FilesystemIterator::SKIP_DOTS );

function iterator_check_traversal_callback( $current, $key, $iterator ) {
    $file_name = $current->getFilename();

    // Only include *.php files
    if ( ! $current->isDir() ) {
        return preg_match( '/^.+\.php$/i', $file_name );
    }

    // Don't include the /includes/admin folder when on the public site
    return 'admin' === $file_name
        ? is_admin()
        : true;
}

$iterator_filter = new \RecursiveCallbackFilterIterator(
    $includes_import_root, 'iterator_check_traversal_callback'
);

foreach ( new \RecursiveIteratorIterator( $iterator_filter ) as $file ) {
    include $file->getRealPath();
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.