web-dev-qa-db-ja.com

レッドヘリングエラー:クラス名は有効なオブジェクトまたは文字列でなければなりません

特に奇妙なバグの根底に到達するために2日間費やした後、私は自分の質問が何であるかを投稿し、もし私がそれを書くために時間をかけていたら、それから投稿したいと思いました。他の人々が頭痛を避けるのを助けることの希望。

PHPの致命的なエラー:クラス名は、有効なオブジェクトまたは7522行目の/includes/common.incの文字列である必要があります

これが出発点でした。ローカルホストに自分のサイトの新しいインストールを作成した後、このエラーが(通常は快適なWSODとともに)発生しました。それについての奇妙なことは、私が別のマシンで動作しているほぼ完全に同じセットアップを持っていたことでした。コードはGitHubによって提供されたため、同一であり、データベースは元のサイトからの直接ダンプであり、phpは期待どおりに構成され、バージョンは0.0.5アウト(5.3.15および5.3.10)であり、両方のDrupalsはバージョンでした7.9 ...すべてが機能しているはずです。

この奇妙な点は、サイトがdrush rrの直後に機能するという事実でしたが、drush cc allの直後または更新時に機能しなくなりました。

これが私の同僚を導き、私は円を描いてcommon.incmodules.incをデバッグして、何が起こっているのかについての手がかりを見つけようとしました。発見したのは、リセットレジストリの後に正しいentity_infoアレイが作成されたが、キャッシュをクリアした後は、完全なentity_infoアレイの一部しか取得できなかったということです。

さらに調査すると、次のコード(module.incから取得)が、存在することがわかっている特定のモジュールファイル内の特定の関数を見つけることができないことがかなりばかげて発見されました。

foreach ($list as $module) {
  $include_file = isset($hook_info[$hook]['group']) && 
    module_load_include(
      'inc', $module, $module . '.' . $hook_info[$hook]['group']
    );
  // Since module_hook() may needlessly try to load the include file again,
  // function_exists() is used directly here.
  if (function_exists($module . '_' . $hook)) {
    $implementations[$hook][$module] = $include_file ? $hook_info[$hook]['group'] : FALSE;
  }
}

これは、このコードが実行された時点で、今までに含まれるべきだった特定のファイルが実行されなかったことを意味するだけです。これをさらに不可解にしたのは、キャッシングコードの外部で次のコードを呼び出すと、正しいentity_info配列が返されたということです。

$entity_info = module_invoke_all('entity_info');

これは、ファイルに何の問題もないことを意味し、それは単にキャッシュシステムであるように見えました。かなり長い話を短くするために、コアを変更するものがないため、drupalキャッシングシステム、古き良き試行錯誤に頼り、すべてのモジュールを無効にして1つずつ有効にすることにしました。Great。

これには少し時間がかかりましたが、すべてを1つのビスポークモジュールにまで削り落とし、それを関数呼び出しごとに試行錯誤しました。犯人が出るまで:

/**
 * Helper function to redirect an actual submit to the ajax handler
 */
function microsites__admin_settings_submit(&$form, &$form_state) {
  return microsites__admin_settings__ajax_button(&$form, &$form_state);
}

だから私は私の質問-とは別に、PHP Fatal error: Class name must be a valid object or a stringエラーを引き起こす可能性があるものは-上記の関数から、このような悲惨なエラーを引き起こし、奇妙な方法で何ができるのでしょうか?

3
Pebbl

PHPをしばらく使用している人にとっては、何が問題の原因であるかは明らかですが、Drupalが曲を作ったり、踊ったりしなかった理由は明らかではありません。

基本的に、WSODの原因は、参照によって渡される呼び出し時間を示す2つのアンパサンドです。 PHPでしばらく使用されなくなったもの。彼らがそこにいた理由は、単なるコピー&ペーストのタイプミスでした。

return microsites__admin_settings__ajax_button(&$form, &$form_state);

上記は次のとおりでした。

return microsites__admin_settings__ajax_button($form, $form_state);

上記の変更により、すべてが魔法のように再び機能し始めました。

エラーを追跡していなかったと思い込んでいる人がいるかもしれませんが、可能な限り多くのログを有効にしました... PHPまたはDrupal call time pass by referenceについて不満を述べています。代わりに、Drupalが行うように見えた-そして、私が追加したキャッシュをクリアした後にのみ-不正な/非推奨の構文にヒットした後、他のファイルを含めることができず、不正なentity_infoが作成されました。配列。

私はそれ以来他のファイルでこれをテストしました、そしてそれが取るすべてはcall time pass by referenceを追加することであり、それは少なくともとにかく私のすべてのlocalhostセットアップに対してDrupalを壊します...つまり、以下がphpで指定されていない限り.ini:

allow_call_time_pass_reference = On

沈黙の理由(他のすべてのPHPエラーと警告が表示されていることを考慮)、他のファイルの読み込みが中断された理由、およびキャッシュがクリアされた後に実行したときにのみ問題が影響を受ける理由について誰かが確認できる場合は、聞いて興味があります。

おそらく7.22にアップグレードすると、上記のいくつかが修正されます...しかし、将来的には、php.ini全体の一貫性を再確認するようにします。つまようじだけを使用してレンガの壁を打ち破ろうとする数日を費やすよりも。

更新

Cliveの質問に答えるために、これは同じ問題をもう一度テストした後のApacheエラーログの出力でした。

[Thu Apr 11 11:15:29 2013] [notice] Digest: generating secret for digest authentication ...
[Thu Apr 11 11:15:29 2013] [notice] Digest: done
[Thu Apr 11 11:15:29 2013] [notice] Apache/2.2.17 (Unix) mod_ssl/2.2.17 OpenSSL/0.9.8r DAV/2 PHP/5.3.6 configured -- resuming normal operations
4
Pebbl

これは、この問題に関する氷山の一角です。 (PHP致命的なエラー:クラス名は、有効なオブジェクトまたは7522行の/includes/common.incの文字列である必要があります)。

7.28の7855行目で、6 =

    $class = $type_info['controller class'];
    $controllers[$entity_type] = new $class($entity_type);  

この問題は、何らかの理由で、module_invoke_all( 'entity_info')を実行して$ type_info配列から$ class値を取得できないことが原因です。確かに、参照による呼び出し時間の経過はそれを行うことができますが、それは通常キャッチされます。実際の問題は7644の/includes/common.incにあります。

  if (empty($entity_info)) {
    if ($cache = cache_get("entity_info:$langcode")) {
      $entity_info = $cache->data;
    }
    else {
      $entity_info = module_invoke_all('entity_info');
      // Merge in default values.
      foreach ($entity_info as $name => $data) {
        $entity_info[$name] += array(
          'fieldable' => FALSE,
          'controller class' => 'DrupalDefaultEntityController',
          'static cache' => TRUE,
          'field cache' => TRUE,
          'load hook' => $name . '_load',
          'bundles' => array(),
          'view modes' => array(),
          'entity keys' => array(),
          'translation' => array(),
        );
        $entity_info[$name]['entity keys'] += array(
          'revision' => '',
          'bundle' => '',
        );
        foreach ($entity_info[$name]['view modes'] as $view_mode => $view_mode_info) {
          $entity_info[$name]['view modes'][$view_mode] += array(
            'custom settings' => FALSE,
          );
        }
        // If no bundle key is provided, assume a single bundle, named after
        // the entity type.
        if (empty($entity_info[$name]['entity keys']['bundle']) && empty($entity_info[$name]['bundles'])) {
          $entity_info[$name]['bundles'] = array($name => array('label' => $entity_info[$name]['label']));
        }
        // Prepare entity schema fields SQL info for
        // DrupalEntityControllerInterface::buildQuery().
        if (isset($entity_info[$name]['base table'])) {
          $entity_info[$name]['schema_fields_sql']['base table'] = drupal_schema_fields_sql($entity_info[$name]['base table']);
          if (isset($entity_info[$name]['revision table'])) {
            $entity_info[$name]['schema_fields_sql']['revision table'] = drupal_schema_fields_sql($entity_info[$name]['revision table']);
          }
        }
      }
      // Let other modules alter the entity info.
      drupal_alter('entity_info', $entity_info);
      cache_set("entity_info:$langcode", $entity_info);
    }
  }

If atで条件がtrueの場合、

if ($cache = cache_get("entity_info:$langcode")) {
      $entity_info = $cache->data;
}

その後、module_invoke_all( 'entity_info')は決して実行されません。キャッシュを作成するために他の処理が実行された後、条件が常に満たされるということが起こります。つまり、entity_infoを設定する方法はありません---私の場合、「ユーザー」エンティティ情報を取得する方法がなかったため、上記の元のエラーが発生しました。私はテストのために 'else'を1レベル高くし( 'else {}'を取り出しました)、すべてがエラーをクリアするために必要なように機能しました。

本当のバグは、キャッシュのチェックが十分ではなく、検索されている特定のエンティティに関するentity_infoの存在のチェックが必要であるということです。実際、キャッシュに格納されているentity_infoでそのエンティティの要素['controller class']を検索することも正当化できる場合があります。 D

2
dvand