プロジェクト

全般

プロフィール

Backport(バックポート) #2718

デイリーニュースにガジェットが追加されている場合、デイリーニュース配信タスク実行時にエラーになる

Yuya Watanabeほぼ6年前に追加. ほぼ6年前に更新.

ステータス:
Fixed(完了)
優先度:
Normal(通常)
担当者:
対象バージョン:
開始日:
2010-09-17
期日:
進捗率:

100%


説明

Overview (現象)

デイリーニュースに1つ以上ガジェットが登録されている場合、
デイリーニュース配信タスクを実行するとエラーが表示され、デイリーニュースが1通も送信されない。

実行するタスク
$./symfony openpne:send-daily-news
表示されるエラー
Call to undefined method myUser::getMember.

再現バージョン

OpenPNE
  • OpenPNE 3.7.0-dev
  • OpenPNE 3.6beta12
php
  • PHP 5.2.13
  • PHP 5.3.3
  • PHP 5.3.5

Causes (原因)

Way to fix (修正内容)

報告元

http://sns.openpne.jp/diary/25133 より転載

デイリーニュースが送れない、です。
PHP5.3.3の所為??それとも、3.7.0-devだから??

【環境】
Powered by OpenPNE 3.7.0-dev
# ./symfony plugin:list
Installed plugins:
symfony 1.4.6-stable
openpne 3.7.0dev-beta
opAuthMailAddressPlugin 1.3.1-devel
opAuthMobileUIDPlugin 1.3.0-devel
opAuthOpenIDPlugin 1.3.0-beta
opCommunityTopicPlugin 1.0.0.2-stable
opWebAPIPlugin 0.4.0-beta
opDiaryPlugin 1.3.1-beta
opBlogPlugin 1.0.1-stable
opOpenSocialPlugin 1.2.0.1-stable
opAshiatoPlugin 0.9.1-stable
opMessagePlugin 0.9.1-beta
opAlbumPlugin 0.9.4-beta
opIntroFriendPlugin 0.9.0.1-beta
opFavoritePlugin 1.0.0.3-beta
opRankingPlugin 1.0.0-beta

FreeBSD 7.2-RELEASE-p8
Apache/2.2.16 (FreeBSD)
PHP 5.3.3 with Suhosin-Patch Zend Engine v2.3.0
mysql 5.1.36

【現象】
コマンドラインで
#./symfony openpne:send-daily-news
を実行すると

Call to undefined method myUser::getMember.

というエラーがでて、デイリーニュースが送れない。

原因

下記コマンドを実行した時にデイリーニュース用のガジェットが表示可能かのロジックが正しくない.

  $ ./symfony openpne:send-daily-news

lib/task/openpneSendDailyNewsTask.class.php の下記部分が実行され,61 行目が実行される.

 27   protected function execute($arguments = array(), $options = array())
 28   {
...
 56       $filteredGadgets = array();
 57       if ($gadgets)
 58       {
 59         foreach ($gadgets as $gadget)
 60         {
 61           if ($gadget->isEnabled())
 62           {
 63             $filteredGadgets[] = array(
 64               'component' => array('module' => $gadget->getComponentModule(), 'action' => $gadget->getComponentAction()),
 65               'gadget' => $gadget,
 66               'member' => $member,
 67             );
 68           } 
 69         } 
 70       } 

ここで isEnabled() を見てみると,sfContext で得られる getUser() で使えるかどうかを決定している.しかし,タスクで実行しているためここで得られる User は実際にメールを送信したい Member を含む User ではなくタスクを実行した時の User (ここでは apps/api/lib/myUser.class.php )である.エラー自体はここで User から getMember() を呼び出すことができないという問題であるが,エラーが発生していなくても正しく動作しないものと思われる.

lib/model/doctrine/Gadget.class.php

 66   public function isEnabled()
 67   {
 68     $list = $this->getGadgetConfigList();
 69     if (empty($list[$this->name]))
 70     {
 71       return false;
 72     }
 73 
 74     $controller = sfContext::getInstance()->getController();
 75     if (!$controller->componentExists($this->getComponentModule(), $this->getComponentAction()))
 76     {
 77       return false;
 78     }
 79 
 80     $member = sfContext::getInstance()->getUser()->getMember();
 81     $isEnabled = $this->isAllowed($member, 'view');
 82 
 83     return $isEnabled;
 84   }


関連するチケット

関連している OpenPNE 3 - Bug(バグ) #1594: デイリーニュースにガジェットが追加されている場合、デイリーニュース配信タスク実行時にエラーになる Fixed(完了) 2010-09-17

関係しているリビジョン

リビジョン ec98cc46 (差分)
Yuya Watanabeほぼ6年前に追加

(fixes #2718, BP from #1594) fixed not to ocurr an error in send daily news mail

BP from #1594
d67e117ceb908f029519712d4007a25ddcb5e2b3

リビジョン c9b31449 (差分)
Yuya Watanabeほぼ6年前に追加

(fixes #2718, BP from #1594) fixed to enable to send mobile daily news using gadget

BP from #1594
7f1145dd3cca38ee7cc2bb9b9c3a149d8c638b3f

リビジョン 8e01ea14 (差分)
Yuya Watanabeほぼ6年前に追加

(fixes #2718, BP from #1594) fixed not to occur the error when the first member 's address is pc

BP from #1594
6436ab559be6df2a32194da119feb61cdd061614

リビジョン 0ffed02f (差分)
Yuya Watanabeほぼ6年前に追加

(fixes #2718, BP from #1594) improved code readability

BP from #1595
d5e20d03ae8ce9bb89028cbd73945746f6240dd5

リビジョン 56506da8 (差分)
Yuya Watanabeほぼ6年前に追加

(refs #2718, BP from #1594) removed unnesesary line

BP from #1594
a6ae31b3ea7c781e21a4cac5089983df3d7a40a8

リビジョン 7b43e438 (差分)
Yuya Watanabeほぼ6年前に追加

(fixes #2718, BP from #1594) fixed to flip Gadget content

BP from #1594
84535af5e7d60ed5beae11d62737f47b03b95912

リビジョン 6dbad96b (差分)
Yuya Watanabeほぼ6年前に追加

(fixes #2718, BP from #1594) fixed coding standard

BP from #1594
d6937c1c57ec8e25a9223592ea372c1d5cab7615

履歴

#1 Yuya Watanabeほぼ6年前に更新

  • ステータスNew(新規) から Accepted(着手) に変更
  • 担当者Yuya Watanabe にセット

#2 Yuya Watanabeほぼ6年前に更新

調査途中経過

3.6.1 での調査内容. 3.7.0-dev でも調査して同様ならばバックポート元にも記載します.

下記コマンドを実行して報告内容のエラーが発生するまでの流れ.

  $ ./symfony openpne:send-daily-news

lib/task/openpneSendDailyNewsTask.class.php の下記部分が実行され,61 行目が実行される.

 27   protected function execute($arguments = array(), $options = array())
 28   {
...
 56       $filteredGadgets = array();
 57       if ($gadgets)
 58       {
 59         foreach ($gadgets as $gadget)
 60         {
 61           if ($gadget->isEnabled())
 62           {
 63             $filteredGadgets[] = array(
 64               'component' => array('module' => $gadget->getComponentModule(), 'action' => $gadget->getComponentAction()),
 65               'gadget' => $gadget,
 66               'member' => $member,
 67             );
 68           } 
 69         } 
 70       } 

lib/model/doctrine/Gadget.class.php の 80 行目でエラーが発生している.

 66   public function isEnabled()
 67   {
 68     $list = $this->getGadgetConfigList();
 69     if (empty($list[$this->name]))
 70     {
 71       return false;
 72     }
 73 
 74     $controller = sfContext::getInstance()->getController();
 75     if (!$controller->componentExists($this->getComponentModule(), $this->getComponentAction()))
 76     {
 77       return false;
 78     }
 79 
 80     $member = sfContext::getInstance()->getUser()->getMember();
 81     $isEnabled = $this->isAllowed($member, 'view');
 82 
 83     return $isEnabled;
 84   }

上記 80 行目で得られるクラスは以下のものである.

apps/api/lib/myUser.class.php

<?php

class myUser extends sfBasicSecurityUser
{
}

#3 Yuya Watanabeほぼ6年前に更新

  • 説明 を更新 (diff)

原因

下記コマンドを実行した時にデイリーニュース用のガジェットが表示可能かのロジックが正しくない.

  $ ./symfony openpne:send-daily-news

lib/task/openpneSendDailyNewsTask.class.php の下記部分が実行され,61 行目が実行される.

 27   protected function execute($arguments = array(), $options = array())
 28   {
...
 56       $filteredGadgets = array();
 57       if ($gadgets)
 58       {
 59         foreach ($gadgets as $gadget)
 60         {
 61           if ($gadget->isEnabled())
 62           {
 63             $filteredGadgets[] = array(
 64               'component' => array('module' => $gadget->getComponentModule(), 'action' => $gadget->getComponentAction()),
 65               'gadget' => $gadget,
 66               'member' => $member,
 67             );
 68           } 
 69         } 
 70       } 

ここで isEnabled() を見てみると,sfContext で得られる getUser() で使えるかどうかを決定している.しかし,タスクで実行しているためここで得られる User は実際にメールを送信したい Member を含む User ではなくタスクを実行した時の User (ここでは apps/api/lib/myUser.class.php )である.エラー自体はここで User から getMember() を呼び出すことができないという問題であるが,エラーが発生していなくても正しく動作しないものと思われる.

lib/model/doctrine/Gadget.class.php

 66   public function isEnabled()
 67   {
 68     $list = $this->getGadgetConfigList();
 69     if (empty($list[$this->name]))
 70     {
 71       return false;
 72     }
 73 
 74     $controller = sfContext::getInstance()->getController();
 75     if (!$controller->componentExists($this->getComponentModule(), $this->getComponentAction()))
 76     {
 77       return false;
 78     }
 79 
 80     $member = sfContext::getInstance()->getUser()->getMember();
 81     $isEnabled = $this->isAllowed($member, 'view');
 82 
 83     return $isEnabled;
 84   }

修正案

  1. 修正案1: Gadget の isEnabled() メソッドを用いるのではなく直接 isAllowed() で表示するかどうかを決定する.
    diff --git a/lib/task/openpneSendDailyNewsTask.class.php b/lib/task/openpneSendDailyNewsTask.class.php
    index f70f082..890ec4a 100644
    --- a/lib/task/openpneSendDailyNewsTask.class.php
    +++ b/lib/task/openpneSendDailyNewsTask.class.php
    @@ -58,7 +58,7 @@ EOF;
           {
             foreach ($gadgets as $gadget)
             {
    -          if ($gadget->isEnabled())
    +          if ($gadget->isAllowed($member, 'view'))
               {
                 $filteredGadgets[] = array(
                   'component' => array('module' => $gadget->getComponentModule(), 'action' => $gadget->getComponentAction()),
    
  2. 修正案2: 表示可能かどうかを見たいメンバを Gadgetの isEnable() メソッドの引数に与えるようにする.
    diff --git a/lib/model/doctrine/Gadget.class.php b/lib/model/doctrine/Gadget.class.php
    index 4cb5053..80ab1cd 100644
    --- a/lib/model/doctrine/Gadget.class.php
    +++ b/lib/model/doctrine/Gadget.class.php
    @@ -63,7 +63,7 @@ class Gadget extends BaseGadget implements opAccessControlRecordInterface
         return $list[$this->name]['component'][1];
       }
    
    -  public function isEnabled()
    +  public function isEnabled($member = null)
       {
         $list = $this->getGadgetConfigList();
         if (empty($list[$this->name]))
    @@ -77,7 +77,10 @@ class Gadget extends BaseGadget implements opAccessControlRecordInterface
           return false;
         }
    
    -    $member = sfContext::getInstance()->getUser()->getMember();
    +    if (is_null($member))
    +    {
    +      $member = sfContext::getInstance()->getUser()->getMember();
    +    }
         $isEnabled = $this->isAllowed($member, 'view');
    
         return $isEnabled;
    

備考

下記 myUser クラスは sfBasicSecurityUser を継承しているが,他のアプリケーションの myUser と同様に OpenPNE 内の User クラスを継承したほうがよいか?

apps/api/lib/myUser.class.php

  1 <?php
  2 
  3 class myUser extends sfBasicSecurityUser
  4 {
  5 }

  • 参考

apps/pc_frontend/lib/myUser.class.php

 10 
 11 class myUser extends opSecurityUser
 12 {

apps/pc_backend/lib/myUser.class.php

 10 
 11 class myUser extends opBaseSecurityUser
 12 {

apps/mobile_frontend/lib/myUser.class.php

 10 
 11 class myUser extends opSecurityUser
 12 {

apps/mobile_mail_frontend/lib/myUser.class.php

 10 
 11 class myUser extends sfBasicSecurityUser
 12 {

#4 Yuya Watanabeほぼ6年前に更新

今回は note-3 に記述された2つの修正案のうち2番目を採用する.

#5 Yuya Watanabeほぼ6年前に更新

  • ステータスAccepted(着手) から Pending Review(レビュー待ち) に変更
  • 進捗率0 から 50 に変更

更新履歴 ec98cc465d39d414bd65435e95f459905936dafa で適用されました。

#6 Kousuke Ebiharaほぼ6年前に更新

  • ステータスPending Review(レビュー待ち) から Pending Testing(テスト待ち) に変更
  • 進捗率50 から 70 に変更

#7 Yuma Sakataほぼ6年前に更新

  • ステータスPending Testing(テスト待ち) から Rejected(差し戻し) に変更
  • 進捗率70 から 50 に変更

テスト実施しましたが、気になる点がありましたので確認お願いします。

フリーエリアガジェットを追加した場合、携帯メールアドレス宛てにデイリーニュース送信できるか確認

  • 試験手順
    1. 管理画面のデイリーニュース(携帯メールアドレス向け)ガジェット設定ページ(/pc_backend.php/design/gadget)で、フリーエリアガジェットを追加する
    2. サーバーの 3.6 ディレクトリにアクセスする
    3. $./symfony openpne:send-daily-news 実行する
  • 試験結果
    以下エラーが出て携帯メールアドレス宛てにデイリーニュース送信できない。
    PHP Fatal error:  Cannot redeclare class defaultComponents in /home/hoge/sns/36.example.com/apps/mobile_frontend/modules/default/actions/components.class.php on line 50
    
  • 修正方針
    フリーエリアガジェットを追加した場合、携帯メールアドレス宛てにデイリーニュース送信できるように修正お願いします。

#8 Yuya Watanabeほぼ6年前に更新

  • ステータスRejected(差し戻し) から Accepted(着手) に変更
  • 進捗率50 から 0 に変更

問題2

  1. 携帯メールアドレスのみを持つメンバを追加する
  2. 携帯デイリーニュースにガジェットを追加する
  3. 「symfony openpne:send-daily-news」を実行する
    • 下記エラーが発生する
      PHP Fatal error:  Cannot redeclare class defaultComponents in /home/hoge/sns/36.example.com/apps/mobile_frontend/modules/default/actions/components.class.php on line 50
      

原因

$gadget->isEnabled() の中の sfContext::getInstance()->getController()->componentExists() でコンポーネントの存在確認される際にロードされるコンポーネントのアプリケーションが実際に送信する際のコンテキストとは別の場合があることが原因であると思われる.そのため,最初に pc_frontend で sfContext::createInstance() が呼び出されているので携帯メールアドレス向けにデイリー・ニュースを送信しようとするとエラーが発生する.直前の sfContext::createInstance() と componentExists() のインスタンスが一致する場合はエラーが発生しないため,PCメールアドレスに送信するときにエラーが発生しない.

具体的には,member_id=1 のメンバが $member->getEmailAddress() でPCメールアドレスを取得でき,member_id=2 のメンバが携帯メールアドレスを取得できるとするとき,下記のような感じで直前に生成されたコンテキストと送信時のコンテキストが一段階ずつずれている.
  1. 最初にpc_frontendがコンテキストで設定される (openpneSendDailyNewsTask.class.php 31行目)
    • componentExists時: 未 送信時: 未
  2. componentExists() が呼び出される (openpneSendDailyNewsTask.class.php 61行目)
    • componentExists時: pc_frontend 送信時: 未
  3. opBaseMailTask::getContextByEmailAddress() 内で sfContext::createInstance() が呼び出される (openpneSendDailyNewsTask.class.php 72行目)
    • componentExists時: pc_frontend 送信時: pc_frontend
  4. opMailSend::sendTemplateMail() が呼び出される (openpneSendDailyNewsTask.class.php 80行目)
    • componentExists時: pc_frontend 送信時: pc_frontend -> メールが送信される
  5. opBaseMailTask::getContextByEmailAddress() 内で sfContext::createInstance() が呼び出される (openpneSendDailyNewsTask.class.php 72行目)
    • componentExists時: pc_frontend 送信時: mobile_frontend
  6. opMailSend::sendTemplateMail() が呼び出される (openpneSendDailyNewsTask.class.php 80行目)
    • componentExists時: pc_frontend 送信時: mobile_frontend -> エラーが発生する

lib/task/openpneSendDailyNewsTask.class.php

 30 
 31     sfContext::createInstance($this->createConfiguration('pc_frontend', 'prod'), 'pc_frontend');
 32
...
 60         {
 61           if ($gadget->isEnabled($member))
 62           {
...
 71       
 72       $context = $this->getContextByEmailAddress($address);
 73       $params = array(
 74         'member'  => $member,
 75         'gadgets' => $filteredGadgets,
 76         'subject' => $context->getI18N()->__('デイリーニュース'),
 77         'today'   => time(),
 78       );
 79
 80       opMailSend::sendTemplateMail('dailyNews', $address, opConfig::get('admin_mail_address'), $params, $context);

lib/task/opBaseSendMailTask.class.php

 44   protected function getContextByEmailAddress($address)
 45   {
 46     $application = 'pc_frontend';
 47     if (opToolkit::isMobileEmailAddress($address))
 48     {
 49       $application = 'mobile_frontend';
 50     }
 51 
 52     if (!sfContext::hasInstance($application))
 53     {
 54       $context = sfContext::createInstance($this->createConfiguration($application, 'prod'), $application);
 55     }
 56     else
 57     {
 58       $context = sfContext::getInstance($application);
 59     }
 60 
 61     return $context;
 62   }

修正案2

コンテキストの変更を 送信時ではなく $gadget->isEnabled() よりも前に行うことで原因で発生するような齟齬が発生しなくなる.

diff --git a/lib/task/openpneSendDailyNewsTask.class.php b/lib/task/openpneSendDailyNewsTask.class.php
index 3a7d9a9..e1c511f 100644
--- a/lib/task/openpneSendDailyNewsTask.class.php
+++ b/lib/task/openpneSendDailyNewsTask.class.php
@@ -48,6 +48,7 @@ EOF;
       }
       $address = $member->getEmailAddress();
       $gadgets = $pcGadgets['dailyNewsContents'];
+      $context = $this->getContextByEmailAddress($address);
       if (opToolkit::isMobileEmailAddress($address))
       {
         $gadgets = $mobileGadgets['mobileDailyNewsContents'];
@@ -69,7 +70,6 @@ EOF;
         }
       }

-      $context = $this->getContextByEmailAddress($address);
       $params = array(
         'member'  => $member,
         'gadgets' => $filteredGadgets,

#9 Yuya Watanabeほぼ6年前に更新

  • ステータスAccepted(着手) から Pending Review(レビュー待ち) に変更
  • 進捗率0 から 50 に変更

更新履歴 c9b3144932bb242230d47fd757fc125b7a0551ca で適用されました。

#10 Kousuke Ebiharaほぼ6年前に更新

  • ステータスPending Review(レビュー待ち) から Pending Testing(テスト待ち) に変更
  • 進捗率50 から 70 に変更

#11 Kousuke Ebiharaほぼ6年前に更新

  • ステータスPending Testing(テスト待ち) から Pending Review(レビュー待ち) に変更
  • 進捗率70 から 50 に変更

すいません、間違えてステータスを変更しました。

#12 Yuya Watanabeほぼ6年前に更新

更新履歴 8e01ea145319b53ff4b00c5a457d2c23e0dc52be で適用されました。

#13 Yuya Watanabeほぼ6年前に更新

更新履歴 0ffed02f2333c3ee0640988e0fd676f49ba5569a で適用されました。

#14 Yuya Watanabeほぼ6年前に更新

更新履歴 7b43e4389554892cde00e9da45b10dcbb7a7b9ad で適用されました。

#15 Yuya Watanabeほぼ6年前に更新

更新履歴 6dbad96bf5baefafda4701c93598fed69808f884 で適用されました。

#16 Kousuke Ebiharaほぼ6年前に更新

  • ステータスPending Review(レビュー待ち) から Pending Testing(テスト待ち) に変更
  • 進捗率50 から 70 に変更

#17 Yuma Sakataほぼ6年前に更新

  • ステータスPending Testing(テスト待ち) から Fixed(完了) に変更
  • 進捗率70 から 100 に変更

テストOKです。

他の形式にエクスポート: Atom PDF