2016-07-12 4 views
0

Я пытаюсь установить массив с суммой значений, содержащихся в объекте. Однако, когда одно из свойств («акции» в этом примере) не установлено в объекте, операция должна продолжаться, не вызывая следующую ошибку:Тернарный оператор внутри вычисления PHP

«[ErrorException] Неопределенное свойство: stdClass :: $ shares»

Я пытаюсь безуспешно тройным оператор для достижения этой цели, как показано ниже:

$array = [strtotime('today midnight') => 
      $ExternalPost->reactions->summary->total_count ?: 0 + 
      $ExternalPost->comments->summary->total_count ?: 0 + 
      ($ExternalPost->shares->count ? $ExternalPost->shares->count : 0) 
     ]; 

похоже, что оператор оценивает все, в том числе двух других предшествующих объектов/значения. Как я могу ограничить оцениваемое значение только последним «$ ExternalPost-> share-> count»?

ответ

2

Я рекомендую переписать свою логику, чтобы она не использовала несколько тернарных операторов. PHP comparison operators documentation говорит:

Note:

It is recommended that you avoid "stacking" ternary expressions. PHP's behaviour when using more than one ternary operator within a single statement is non-obvious:

Example #4 Non-obvious Ternary Behaviour

<?php 
// on first glance, the following appears to output 'true' 
echo (true?'true':false?'t':'f'); 

// however, the actual output of the above is 't' 
// this is because ternary expressions are evaluated from left to right 

// the following is a more obvious version of the same code as above 
echo ((true ? 'true' : false) ? 't' : 'f'); 

// here, you can see that the first expression is evaluated to 'true', which 
// in turn evaluates to (bool)true, thus returning the true branch of the 
// second ternary expression. 
?> 

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

1

Нет причин, по которым операция должна продолжаться без этой ошибки, если $ExternalPost->shares не определено. Это должно быть определено, если вы проверяете для $ExternalPost->shares->count

Else, вы можете изменить логику на что-то вроде:

((isset($ExternalPost->shares) && $ExternalPost->shares->count) ? $ExternalPost->shares->count : 0) 
0

PHP выдаст ошибку, если вы пытаетесь получить доступ к свойству объекта или массива ключей это не существует, даже если вы делаете это только для проверки, существует ли он. Вместо этого вы должны использовать property_exists() (для объектов) или array_key_exists() (для массивов), или isset() (для чего угодно). Используя один из этих методов, вы можете проверить, существует ли переменная/свойство/ключ, не выбрасывая ошибку, если это не так.

Ваш пример может быть переписан в виде:

... 
(isset($ExternalPost->shares->count) ? $ExternalPost->shares->count : 0) 
+0

Если '$ ExternalPost-> shares-> count' является' NULL', то 'isset (...)' вернет 'FALSE', что приведет к тому, что тернарный оператор вернет последний' 0'. Это соответствует результату исходного кода. Разве я не ошибаюсь? –

0

Ваш исходный код составляет

if ($ExternalPost->reactions->summary->total_count) { 
    $a = $ExternalPost->reactions->summary->total_count; 
} else { 
    if ($ExternalPost->comments->summary->total_count) { 
     $a = 0 + $ExternalPost->comments->summary->total_count; 
    } elseif($ExternalPost->shares->count) { 
     $a = 0 + $ExternalPost->shares->count 
    } else { 
     $a = 0; 
    } 
} 

Примера http://ideone.com/c9bY5o


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

(isset($ExternalPost->reactions->summary->total_count) ? $ExternalPost->reactions->summary->total_count : 0) + 
(isset($ExternalPost->comments->summary->total_count) ? $ExternalPost->comments->summary->total_count : 0) + 
(isset($ExternalPost->shares->count) ? $ExternalPost->shares->count : 0); 

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

if (isset($ExternalPost->reactions->summary->total_count)) { 
    $a = $ExternalPost->reactions->summary->total_count; 
} else { 
    $a = 0; 
} 
if (isset($ExternalPost->comments->summary->total_count)) { 
    $a += $ExternalPost->comments->summary->total_count; 
} else { 
    $a += 0; 
} 
if (isset($ExternalPost->shares->count)) { 
    $a += $ExternalPost->shares->count; 
} else { 
    $a += 0; 
} 

Пример http://ideone.com/GKkpZw

Обратите внимание, что объект имел reactions и shares свойства удалены.


Что касается рекомендации, то я предлагаю вместо назначать методы к объектам и получить итоговые данные таким образом. Поэтому вам нужно только поддерживать вычисления в одном месте и иметь возможность повторно использовать его в других сценариях.

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

Грубый Ленивый Getter ООП Пример - это может быть переработан, чтобы подсчитывать итоговые данные о настройке значений вместо этого, принимает массив значений и т.д.

class ExternalPost 
{ 
    private $reactions; 

    private $comments; 

    private $shares; 

    public function __construct(Reactions $reactions = null, Comments $comments = null, Shares $shares = null) 
    { 
     $this->reactions = $reactions; 
     $this->comments = $comments; 
     $this->shares = $shares; 
    } 

    public function getReactions() 
    { 
     if (null === $this->reactions) { 
      $this->reactions = new Reactions; 
     } 
     return $this->reactions; 
    } 

    public function getComments() 
    { 
     if (null === $this->comments) { 
      $this->comments = new Comments; 
     } 
     return $this->comments; 
    } 

    public function getShares() 
    { 
     if (null === $this->shares) { 
      $this->shares = new Shares; 
     } 
     return $this->shares; 
    } 

    public function getTotal() 
    { 
     return $this->getReactions()->getTotal() + 
      $this->getComments()->getTotal() + 
      $this->getShares()->getCount(); 
    } 
} 
class Summary 
{ 
    private $total_count = 0; 

    public function getTotalCount() 
    { 
     return $this->total_count; 
    } 

    public function setTotalCount($total) 
    { 
     $this->total_count = $total; 

     return $this; 
    } 
} 
abstract class Summation 
{ 
    protected $summary; 

    public function __construct(Summary $summary = null) 
    { 
     $this->summary = $summary; 
    } 

    public function getSummary() 
    { 
     if (null === $this->summary) { 
      $this->summary = new Summary; 
     } 

     return $this->summary; 
    } 

    public function getTotal() 
    { 
     return $this->getSummary()->getTotalCount(); 
    } 

    public function setTotal($total) 
    { 
     $this->getSummary()->setTotalCount($total); 

     return $this; 
    } 
} 

class Reactions extends Summation{} 

class Comments extends Summation{} 
class Shares 
{ 
    private $count = 0; 

    public function getCount() 
    { 
     return $this->count; 
    } 

    public function setCount($count) 
    { 
     $this->count = $count; 

     return $this; 
    } 
} 

Так что теперь код в ваш скрипт будет выглядеть

$reactions = new Reactions; 
$reactions->setTotal(1); 
$comments = null; 
/* 
$comments = new Comments; 
$comments->setTotal(2); 
//alternative to using getComments below 
*/ 

$ExternalPost = new ExternalPost($reactions, $comments); 
/* $ExternalPost->getReactions()->setTotal(1); 
    //alternative to defining $reactions above */ 
$ExternalPost->getComments()->getSummary()->setTotalCount(2); 
$ExternalPost->getShares()->setCount(3); 

$array = [strtotime('today midnight') => $ExternalPost->getTotal()]; 

Пример http://ideone.com/oqSYNd

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

Смежные вопросы