Почему WP Filesystem API не может прочитать googlefonts.json?
Я унаследовал сайт, построенный на теме Highgrade (southcentral), которая использует фреймворк Redux.
В интерфейсе и админ-панели появляется следующая ошибка:
Warning: Invalid argument supplied for foreach() in /Volumes/Data/Users/me/Sites/reference360.eu/wordpress/wp-content/themes/southcentral/highgrade/framework/inc/fields/typography/field_typography.php on line 772
Я пробовал изменять права доступа и владельца для googlefonts.json, но безрезультатно.
Проблема, похоже, кроется в фреймворке Redux - см. эту ветку на github.
Отладка в классе typography.php:
if (!isset($this->parent->fonts['google']) || empty($this->parent->fonts['google'])) {
$this->parent->fonts['google'] = json_decode($wp_filesystem->get_contents(ReduxFramework::$_dir . 'inc/fields/typography/googlefonts.json'), true);
var_dump(ReduxFramework::$_dir . 'inc/fields/typography/googlefonts.json');
var_dump($wp_filesystem->get_contents(ReduxFramework::$_dir . 'inc/fields/typography/googlefonts.json')); exit;
$this->parent->font_groups['google'] = array(
'id' => 'google',
'text' => __('Google Webfonts', 'redux-framework'),
'children' => array(),
);
foreach ($this->parent->fonts['google'] as $font => $extra) {
$this->parent->font_groups['google']['children'][] = array(
'id' => $font,
'text' => $font
);
}
}
}
У кого-нибудь есть идеи, что может вызывать эту проблему? Файл существует и находится по указанному пути. Среда разработки - OSX Mavericks.
ОБНОВЛЕНИЕ:
Изменение владельца всей директории WordPress на _www:_www
решает проблему, но это очевидно не самое лучшее решение.

Не обязательно использовать WP_Filesystem для каждой мелочи, и в данном случае правильное решение — использовать обычный file_get_contents
.
WP_Filesystem — это обёртка над различными способами безопасного взаимодействия с файловой системой... но она не предназначена для всего.
По сути, код WP_Filesystem был создан, чтобы позволить WordPress обновлять себя.
В таких случаях владение файлами имеет большое значение. Многие серверы работают от имени пользователя "www" (например), а не реального владельца PHP-файлов. Если бы WordPress записывал файл напрямую в таком случае, результирующий файл принадлежал бы "www", а не настоящему владельцу. Это может привести к проблемам безопасности, особенно на shared-хостинге.
Поэтому WP_Filesystem абстрагирует файловые операции. Он может читать и записывать файлы способами, которые сохранят владение файлами. Если он может записать напрямую и владение останется корректным, он сделает это. Но если нет — ему потребуются учётные данные для какого-либо метода, например FTP. Используя учётные данные, он может войти через этот путь и записать файлы от имени правильного владельца.
Это означает, что перед использованием WP_Filesystem его нужно настроить. Необходимо выполнить тест, и если тест не пройден, то нужно получить учётные данные от пользователя. Без них он не сможет работать.
В вашем случае, поскольку «изменение владельца всей директории WordPress на _www:_www» решило проблему, это именно то, что происходит. Тест на запись файла с сохранением владения не проходит. Изменяя владельца существующих файлов, вы меняете условия для этого теста.
Но правда в том, что в данном случае нет причин использовать WP_Filesystem вообще. Вы читаете файл. Вы можете прочитать его напрямую. Владение здесь не имеет значения. Поэтому действительно стоит просто использовать file_get_contents
. Использование WP_Filesystem для этого бессмысленно.
Подробнее о WP_Filesystem с точки зрения его использования: http://ottopress.com/2011/tutorial-using-the-wp_filesystem/

Я нахожу это немного противоречивым — выступать за использование file_get_contents()
, в то время как оно запрещено правилами репозитория. Что если это публичная тема в репозитории? Я понимаю, что темы вне репозитория могут (и делают, включая меня) просто "забить" на это правило, но думаю, что это важный контекст для ответа, и случай с темой в репозитории не покрывается рекомендацией использовать эту функцию.

@Otto, значит, WP Filesystem выполняет тест на запись файла перед тем, как сделать $wp_filesystem->get_contents(ReduxFramework::$_dir . 'inc/fields/typography/googlefonts.json') в моём случае? Если бы я знал, куда он пытается записать файл, я мог бы просто изменить права на эту директорию, так что эта информация была бы полезной

Также, похоже, что всё, что делает WP_Filesystem_Direct::file_get_contents — это обёртка с подавлением ошибок над php-шной функцией file_get_contents()

@codecowboy Глобальная переменная global $wp_filesystem
не будет установлена до тех пор, пока вы не вызовете функцию WP_Filesystem()
, которая фактически выполняет проверку для каждого из доступных методов. Если прямой метод работает, то он назначается этой переменной. Поэтому вы не можете вызывать $wp_filesystem->get_contents()
, пока не вызовите WP_Filesystem() хотя бы один раз.

@Rarst Если это касается темы в репозитории, я бы рекомендовал полностью переписать это, чтобы не приходилось каждый раз читать и парсить этот .json файл. Вместо того чтобы оставлять его как json файл, вы можете прочитать его один раз, пропустить через json_decode, вывести результат через print_r и поместить это в PHP файл, который затем можно просто подключить для настройки любой переменной, которую он содержит. Зачем каждый раз тратить время на чтение и парсинг файла, если можно сделать это всего один раз?

@Rarst Что касается более общего случая "чтения файла в теме", я не могу придумать действительно веской причины, по которой тема должна это делать. Мы разрешаем использовать file()
специально для таких случаев, но, по правде говоря, это возникало всего несколько раз, и в тех случаях было относительно просто использовать альтернативный подход, который устранял необходимость чтения файла вообще.

@Otto Я имел в виду, что в форме "использовать обычный file_get_contents()
" ответу не хватает контекста, например, правил репозитория. Если кто-то просто использует это, а потом получает отказ при попытке отправить в репозиторий — они посчитают такой совет плохим.

@Rarst Я понимаю твою точку зрения, но не согласен с ней. Он задал вопрос о конкретном фрагменте кода. Мой ответ отражает только это. Он не задавал вопрос, который потребовал бы гораздо более сложного ответа с проверкой темы, плагином Theme Check и официальным бесплатным репозиторием. Он хочет исправить свою проблему и немного узнать о WP_Filesystem, а не понять, как работает вся структура.

@codecowboy, какую версию Redux вы используете? В новых версиях мы даже не используем googlefonts.json подобным образом.
Я рекомендую вам просто установить Redux Framework из репозитория плагинов WordPress (http://wordpress.org/plugins/redux-framework/), и эта проблема решится, так как плагин переопределит версию Redux, встроенную в вашу тему.

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

После некоторой отладки выяснилось, что на самом деле WP_Filesystem пытается установить FTP-соединение с временным файлом во время выполнения проверок:
public function get_contents( $file ) {
$tempfile = wp_tempnam($file); // Создаем временный файл
$temp = fopen($tempfile, 'w+'); // Открываем временный файл для записи
if ( ! $temp )
return false; // Если не удалось открыть файл, возвращаем false
if ( ! @ftp_fget($this->link, $temp, $file, FTP_BINARY ) ) // Пытаемся получить файл по FTP
return false; // Если не удалось, возвращаем false, и массив Google Fonts остаётся пустым
fseek( $temp, 0 ); // Перемещаем указатель в начало файла
$contents = ''; // Инициализируем переменную для содержимого
while ( ! feof($temp) ) // Читаем файл до конца
$contents .= fread($temp, 8192); // Добавляем прочитанные данные в переменную
fclose($temp); // Закрываем файл
unlink($tempfile); // Удаляем временный файл
return $contents; // Возвращаем содержимое файла
}
На моей локальной машине с OSX это не работает, тогда как на целевом сервере всё проходит. Не совсем понятно, почему изменение владельца всей директории Wordpress на _www:_www
позволяет ftp_fget
отработать успешно, учитывая, что он пытается прочитать временный файл в /var/tmp
.

Для сведения, последняя версия Redux больше не использует этот конкретный метод для загрузки шрифтов Google. Во-вторых, Отто питает определенную неприязнь к ребятам из Redux, так что воспринимайте его слова с долей скепсиса. Мы постоянно видим ошибки в проверке темы, которые гласят:
ПРЕДУПРЕЖДЕНИЕ: функция file_put_contents найдена в файле class.redux_filesystem.php. Для работы с файлами следует использовать методы WP_Filesystem вместо прямых вызовов файловой системы PHP.
Серьёзно? То есть, Отто? Ты придумываешь стандарты и рекомендации на ходу? В этом есть смысл, учитывая, что вся основа $wp_filesystem выглядит так, будто её собирали по кусочкам дети, которые не имели ни малейшего понятия, что они делают. Какие уж там стандарты!

Я говорил не напрямую о вашей теме, а о предоставленном коде. Я ничего не знаю о вашем фреймворке как таковом. Никогда не изучал его подробно.

Кроме того, использование file_get_contents
не разрешено для тем в каталоге WordPress.org, в основном потому что множество вредоносных программ используют эту функцию. Плагин Theme Check специально ориентирован на эти стандарты.
Тем не менее, вместо того чтобы читать .json файл и декодировать его каждый раз, гораздо логичнее один раз декодировать его и включить полученный PHP-массив непосредственно в код.

Нет, это не моя тема, и, честно говоря, сэр, я не верю вам насчёт фреймворка, учитывая недавние события с ним.

http://reduxframework.com/2014/07/sometimes-standards-dont-always-work/

@kprovance Не уверен, что он питает ненависть, он высказывался в общих чертах.
Отто, так как ты предлагаешь нам найти постоянное решение помимо того, что мы уже сделали? Мы пытаемся использовать WP_Filesystem, но если он не срабатывает, используем file_get_contents. Это приемлемо? До сих пор всё работало нормально.

Ребята из Redux могут не соглашаться со мной и использовать то, что лучше всего подходит для них и их пользователей. Меня это совершенно не беспокоит.

Отто, пожалуйста, напишите мне на email или в Skype. dovy@reduxframework.com. Skype: DovyDigital
Я хотел бы разрешить все вопросы и получить ваш честный совет.
