Merge branch '5.3'
authorTim Düsterhus <duesterhus@woltlab.com>
Tue, 23 Feb 2021 14:14:32 +0000 (15:14 +0100)
committerTim Düsterhus <duesterhus@woltlab.com>
Tue, 23 Feb 2021 14:14:32 +0000 (15:14 +0100)
1  2 
wcfsetup/install/files/lib/data/user/object/watch/UserObjectWatchAction.class.php
wcfsetup/install/files/lib/util/ExceptionLogUtil.class.php
wcfsetup/install/lang/de.xml
wcfsetup/install/lang/en.xml

index afc58cac166a4efdbbbd8bd820589c8a29238ac9,77ce4ade08d7a142aaf52543758123c20873f138..dda2b6090b5020c192257c6818fffdd056c08837
@@@ -10,225 -8,192 +10,225 @@@ use wcf\system\WCF
  
  /**
   * Executes watched object-related actions.
 - * 
 - * @author    Alexander Ebert
 - * @copyright 2001-2019 WoltLab GmbH
 - * @license   GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
 - * @package   WoltLabSuite\Core\Data\User\Object\Watch
 - * 
 - * @method    UserObjectWatch                 create()
 - * @method    UserObjectWatchEditor[]         getObjects()
 - * @method    UserObjectWatchEditor           getSingleObject()
 + *
 + * @author  Alexander Ebert
 + * @copyright   2001-2019 WoltLab GmbH
 + * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
 + * @package WoltLabSuite\Core\Data\User\Object\Watch
 + *
 + * @method  UserObjectWatch         create()
 + * @method  UserObjectWatchEditor[]     getObjects()
 + * @method  UserObjectWatchEditor       getSingleObject()
   */
 -class UserObjectWatchAction extends AbstractDatabaseObjectAction {
 -      /**
 -       * object type object
 -       * @var \wcf\data\object\type\ObjectType
 -       */
 -      protected $objectType = null;
 -      
 -      /**
 -       * user object watch object
 -       * @var UserObjectWatch
 -       */
 -      protected $userObjectWatch = null;
 -      
 -      /**
 -       * Validates parameters to manage a subscription.
 -       */
 -      public function validateManageSubscription() {
 -              $this->readInteger('objectID');
 -              $this->readString('objectType');
 -              
 -              // validate object type
 -              $this->objectType = ObjectTypeCache::getInstance()->getObjectTypeByName('com.woltlab.wcf.user.objectWatch', $this->parameters['objectType']);
 -              if ($this->objectType === null) {
 -                      throw new UserInputException('objectType');
 -              }
 -              
 -              // validate object id
 -              $this->objectType->getProcessor()->validateObjectID($this->parameters['objectID']);
 -              
 -              // get existing subscription
 -              $this->userObjectWatch = UserObjectWatch::getUserObjectWatch($this->objectType->objectTypeID, WCF::getUser()->userID, $this->parameters['objectID']);
 -      }
 -      
 -      /**
 -       * Returns a form to manage a subscription.
 -       * 
 -       * @return      array
 -       */
 -      public function manageSubscription() {
 -              WCF::getTPL()->assign([
 -                      'objectType' => $this->objectType,
 -                      'userObjectWatch' => $this->userObjectWatch
 -              ]);
 -              
 -              return [
 -                      'objectID' => $this->parameters['objectID'],
 -                      'objectType' => $this->parameters['objectType'],
 -                      'template' => WCF::getTPL()->fetch('manageSubscription')
 -              ];
 -      }
 -      
 -      /**
 -       * Validates parameters to save subscription state.
 -       */
 -      public function validateSaveSubscription() {
 -              $this->readBoolean('enableNotification');
 -              $this->readBoolean('subscribe');
 -              
 -              $this->validateManageSubscription();
 -      }
 -      
 -      /**
 -       * Saves subscription state.
 -       */
 -      public function saveSubscription() {
 -              // subscribe
 -              if ($this->parameters['subscribe']) {
 -                      // newly subscribed
 -                      if ($this->userObjectWatch === null) {
 -                              UserObjectWatchEditor::createOrIgnore([
 -                                      'notification' => $this->parameters['enableNotification'] ? 1 : 0,
 -                                      'objectID' => $this->parameters['objectID'],
 -                                      'objectTypeID' => $this->objectType->objectTypeID,
 -                                      'userID' => WCF::getUser()->userID
 -                              ]);
 -                      }
 -                      else if ($this->userObjectWatch->notification != $this->parameters['enableNotification']) {
 -                              // update notification type
 -                              $editor = new UserObjectWatchEditor($this->userObjectWatch);
 -                              $editor->update([
 -                                      'notification' => $this->parameters['enableNotification'] ? 1 : 0
 -                              ]);
 -                      }
 -                      
 -                      // reset user storage
 -                      $this->objectType->getProcessor()->resetUserStorage([WCF::getUser()->userID]);
 -              }
 -              else if ($this->userObjectWatch !== null) {
 -                      // unsubscribe
 -                      $editor = new UserObjectWatchEditor($this->userObjectWatch);
 -                      $editor->delete();
 -                      
 -                      // reset user storage
 -                      $this->objectType->getProcessor()->resetUserStorage([WCF::getUser()->userID]);
 -              }
 -              
 -              return [
 -                      'objectID' => $this->parameters['objectID'],
 -                      'objectType' => $this->parameters['objectType'],
 -                      'subscribe' => $this->parameters['subscribe']
 -              ];
 -      }
 -      
 -      /**
 -       * Adds a subscription.
 -       */
 -      public function subscribe() {
 -              $objectType = ObjectTypeCache::getInstance()->getObjectTypeByName('com.woltlab.wcf.user.objectWatch', $this->parameters['data']['objectType']);
 -              
 -              UserObjectWatchEditor::create([
 -                      'userID' => WCF::getUser()->userID,
 -                      'objectID' => intval($this->parameters['data']['objectID']),
 -                      'objectTypeID' => $objectType->objectTypeID,
 -                      'notification' => !empty($this->parameters['enableNotification']) ? 1 : 0
 -              ]);
 -              
 -              // reset user storage
 -              $objectType->getProcessor()->resetUserStorage([WCF::getUser()->userID]);
 -      }
 -      
 -      /**
 -       * Removes a subscription.
 -       */
 -      public function unsubscribe() {
 -              $objectType = ObjectTypeCache::getInstance()->getObjectTypeByName('com.woltlab.wcf.user.objectWatch', $this->parameters['data']['objectType']);
 -              
 -              if ($this->userObjectWatch !== null) $userObjectWatch = $this->userObjectWatch;
 -              else {
 -                      $userObjectWatch = UserObjectWatch::getUserObjectWatch($objectType->objectTypeID, WCF::getUser()->userID, intval($this->parameters['data']['objectID']));
 -              }
 -              $editor = new UserObjectWatchEditor($userObjectWatch);
 -              $editor->delete();
 -              
 -              // reset user storage
 -              $objectType->getProcessor()->resetUserStorage([WCF::getUser()->userID]);
 -      }
 -      
 -      /**
 -       * Validates the subscribe action.
 -       */
 -      protected function __validateSubscribe() {
 -              $this->readInteger('objectID', false, 'data');
 -              $this->readString('objectType', false, 'data');
 -              
 -              // validate object type
 -              $objectType = ObjectTypeCache::getInstance()->getObjectTypeByName('com.woltlab.wcf.user.objectWatch', $this->parameters['data']['objectType']);
 -              if ($objectType === null) {
 -                      throw new UserInputException('objectType');
 -              }
 -              
 -              // validate object id
 -              $objectType->getProcessor()->validateObjectID(intval($this->parameters['data']['objectID']));
 -              
 -              // get existing subscription
 -              $this->userObjectWatch = UserObjectWatch::getUserObjectWatch($objectType->objectTypeID, WCF::getUser()->userID, intval($this->parameters['data']['objectID']));
 -      }
 -      
 -      /**
 -       * Validates the subscribe action.
 -       */
 -      public function validateSubscribe() {
 -              $this->__validateSubscribe();
 -              
 -              if ($this->userObjectWatch !== null) {
 -                      throw new PermissionDeniedException();
 -              }
 -      }
 -      
 -      /**
 -       * Validates the unsubscribe action.
 -       */
 -      public function validateUnsubscribe() {
 -              $this->__validateSubscribe();
 -              
 -              if ($this->userObjectWatch === null) {
 -                      throw new PermissionDeniedException();
 -              }
 -      }
 +class UserObjectWatchAction extends AbstractDatabaseObjectAction
 +{
 +    /**
 +     * object type object
 +     * @var \wcf\data\object\type\ObjectType
 +     */
 +    protected $objectType;
 +
 +    /**
 +     * user object watch object
 +     * @var UserObjectWatch
 +     */
 +    protected $userObjectWatch;
 +
 +    /**
 +     * Validates parameters to manage a subscription.
 +     */
 +    public function validateManageSubscription()
 +    {
 +        $this->readInteger('objectID');
 +        $this->readString('objectType');
 +
 +        // validate object type
 +        $this->objectType = ObjectTypeCache::getInstance()->getObjectTypeByName(
 +            'com.woltlab.wcf.user.objectWatch',
 +            $this->parameters['objectType']
 +        );
 +        if ($this->objectType === null) {
 +            throw new UserInputException('objectType');
 +        }
 +
 +        // validate object id
 +        $this->objectType->getProcessor()->validateObjectID($this->parameters['objectID']);
 +
 +        // get existing subscription
 +        $this->userObjectWatch = UserObjectWatch::getUserObjectWatch(
 +            $this->objectType->objectTypeID,
 +            WCF::getUser()->userID,
 +            $this->parameters['objectID']
 +        );
 +    }
 +
 +    /**
 +     * Returns a form to manage a subscription.
 +     *
 +     * @return  array
 +     */
 +    public function manageSubscription()
 +    {
 +        WCF::getTPL()->assign([
 +            'objectType' => $this->objectType,
 +            'userObjectWatch' => $this->userObjectWatch,
 +        ]);
 +
 +        return [
 +            'objectID' => $this->parameters['objectID'],
 +            'objectType' => $this->parameters['objectType'],
 +            'template' => WCF::getTPL()->fetch('manageSubscription'),
 +        ];
 +    }
 +
 +    /**
 +     * Validates parameters to save subscription state.
 +     */
 +    public function validateSaveSubscription()
 +    {
 +        $this->readBoolean('enableNotification');
 +        $this->readBoolean('subscribe');
 +
 +        $this->validateManageSubscription();
 +    }
 +
 +    /**
 +     * Saves subscription state.
 +     */
 +    public function saveSubscription()
 +    {
 +        // subscribe
 +        if ($this->parameters['subscribe']) {
 +            // newly subscribed
 +            if ($this->userObjectWatch === null) {
-                 UserObjectWatchEditor::create([
++                UserObjectWatchEditor::createOrIgnore([
 +                    'notification' => $this->parameters['enableNotification'] ? 1 : 0,
 +                    'objectID' => $this->parameters['objectID'],
 +                    'objectTypeID' => $this->objectType->objectTypeID,
 +                    'userID' => WCF::getUser()->userID,
 +                ]);
 +            } elseif ($this->userObjectWatch->notification != $this->parameters['enableNotification']) {
 +                // update notification type
 +                $editor = new UserObjectWatchEditor($this->userObjectWatch);
 +                $editor->update([
 +                    'notification' => $this->parameters['enableNotification'] ? 1 : 0,
 +                ]);
 +            }
 +
 +            // reset user storage
 +            $this->objectType->getProcessor()->resetUserStorage([WCF::getUser()->userID]);
 +        } elseif ($this->userObjectWatch !== null) {
 +            // unsubscribe
 +            $editor = new UserObjectWatchEditor($this->userObjectWatch);
 +            $editor->delete();
 +
 +            // reset user storage
 +            $this->objectType->getProcessor()->resetUserStorage([WCF::getUser()->userID]);
 +        }
 +
 +        return [
 +            'objectID' => $this->parameters['objectID'],
 +            'objectType' => $this->parameters['objectType'],
 +            'subscribe' => $this->parameters['subscribe'],
 +        ];
 +    }
 +
 +    /**
 +     * Adds a subscription.
 +     */
 +    public function subscribe()
 +    {
 +        $objectType = ObjectTypeCache::getInstance()->getObjectTypeByName(
 +            'com.woltlab.wcf.user.objectWatch',
 +            $this->parameters['data']['objectType']
 +        );
 +
 +        UserObjectWatchEditor::create([
 +            'userID' => WCF::getUser()->userID,
 +            'objectID' => \intval($this->parameters['data']['objectID']),
 +            'objectTypeID' => $objectType->objectTypeID,
 +            'notification' => !empty($this->parameters['enableNotification']) ? 1 : 0,
 +        ]);
 +
 +        // reset user storage
 +        $objectType->getProcessor()->resetUserStorage([WCF::getUser()->userID]);
 +    }
 +
 +    /**
 +     * Removes a subscription.
 +     */
 +    public function unsubscribe()
 +    {
 +        $objectType = ObjectTypeCache::getInstance()->getObjectTypeByName(
 +            'com.woltlab.wcf.user.objectWatch',
 +            $this->parameters['data']['objectType']
 +        );
 +
 +        if ($this->userObjectWatch !== null) {
 +            $userObjectWatch = $this->userObjectWatch;
 +        } else {
 +            $userObjectWatch = UserObjectWatch::getUserObjectWatch(
 +                $objectType->objectTypeID,
 +                WCF::getUser()->userID,
 +                \intval($this->parameters['data']['objectID'])
 +            );
 +        }
 +        $editor = new UserObjectWatchEditor($userObjectWatch);
 +        $editor->delete();
 +
 +        // reset user storage
 +        $objectType->getProcessor()->resetUserStorage([WCF::getUser()->userID]);
 +    }
 +
 +    /**
 +     * Validates the subscribe action.
 +     */
 +    protected function __validateSubscribe()
 +    {
 +        $this->readInteger('objectID', false, 'data');
 +        $this->readString('objectType', false, 'data');
 +
 +        // validate object type
 +        $objectType = ObjectTypeCache::getInstance()->getObjectTypeByName(
 +            'com.woltlab.wcf.user.objectWatch',
 +            $this->parameters['data']['objectType']
 +        );
 +        if ($objectType === null) {
 +            throw new UserInputException('objectType');
 +        }
 +
 +        // validate object id
 +        $objectType->getProcessor()->validateObjectID(\intval($this->parameters['data']['objectID']));
 +
 +        // get existing subscription
 +        $this->userObjectWatch = UserObjectWatch::getUserObjectWatch(
 +            $objectType->objectTypeID,
 +            WCF::getUser()->userID,
 +            \intval($this->parameters['data']['objectID'])
 +        );
 +    }
 +
 +    /**
 +     * Validates the subscribe action.
 +     */
 +    public function validateSubscribe()
 +    {
 +        $this->__validateSubscribe();
 +
 +        if ($this->userObjectWatch !== null) {
 +            throw new PermissionDeniedException();
 +        }
 +    }
 +
 +    /**
 +     * Validates the unsubscribe action.
 +     */
 +    public function validateUnsubscribe()
 +    {
 +        $this->__validateSubscribe();
 +
 +        if ($this->userObjectWatch === null) {
 +            throw new PermissionDeniedException();
 +        }
 +    }
  }
index 5c9898b2d914e5a0a1230eefb05f4cca1d525372,161d4615fd82967dc8aceb4d5dc3925dba27d4dc..a76d919d89681d2f7ac71d64a4d66b6b277106fe
@@@ -7,122 -5,117 +7,126 @@@ use wcf\system\Regex
  
  /**
   * Contains helper functions to process the Exception log.
 - * 
 - * @author    Tim Duesterhus
 - * @copyright 2001-2019 WoltLab GmbH
 - * @license   GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
 - * @package   WoltLabSuite\Core\Util
 - * @since     5.2
 + *
 + * @author  Tim Duesterhus
 + * @copyright   2001-2019 WoltLab GmbH
 + * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
 + * @package WoltLabSuite\Core\Util
 + * @since   5.2
   */
 -final class ExceptionLogUtil {
 -      /**
 -       * Splits the given string of Exceptions into an array.
 -       * 
 -       * @param       string          $contents
 -       * @return      string[]
 -       */
 -      public static function splitLog($contents) {
 -              // unify newlines
 -              $contents = StringUtil::unifyNewlines($contents);
 -                              
 -              // split contents
 -              $split = new Regex('(?:^|\n<<<<\n\n)(?:<<<<<<<<([a-f0-9]{40})<<<<\n|$)');
 -              $contents = $split->split($contents, Regex::SPLIT_NON_EMPTY_ONLY | Regex::CAPTURE_SPLIT_DELIMITER);
 -              
 -              if (empty($contents)) {
 -                      return [];
 -              }
 -              
 -              // even items become keys, odd items become values
 -              return array_merge(...array_map(
 -                      function($v) {
 -                              return [$v[0] => $v[1]];
 -                      },
 -                      array_chunk($contents, 2)
 -              ));
 -      }
 -      
 -      /**
 -       * Parses the given log entry.
 -       * 
 -       * @param       string          $entry
 -       * @return      mixed[]
 -       */
 -      public static function parseException($entry) {
 -              static $regex = null;
 -              static $chainRegex = null;
 -              if ($regex === null || $chainRegex === null) {
 -                      $regex = new Regex("(?P<date>[MTWFS][a-z]{2}, \d{1,2} [JFMASOND][a-z]{2} \d{4} \d{2}:\d{2}:\d{2} [+-]\d{4})\s*\n".
 -                      "Message: (?P<message>.*?)\s*\n".
 -                      "PHP version: (?P<phpVersion>.*?)\s*\n".
 -                      "WoltLab Suite version: (?P<wcfVersion>.*?)\s*\n".
 -                      "Request URI: (?P<requestURI>.*?)\s*\n".
 -                      "Referrer: (?P<referrer>.*?)\s*\n".
 -                      "User Agent: (?P<userAgent>.*?)\s*\n".
 -                      "Peak Memory Usage: (?<peakMemory>\d+)/(?<maxMemory>(?:\d+|-1))\s*\n".
 -                      "(?<chain>======\n".
 -                      ".*)", Regex::DOT_ALL);
 -                      $chainRegex = new Regex("======\n".
 -                      "Error Class: (?P<class>.*?)\s*\n".
 -                      "Error Message: (?P<message>.*?)\s*\n".
 -                      "Error Code: (?P<code>[a-zA-Z0-9]+)\s*\n".
 -                      "File: (?P<file>.*?) \((?P<line>\d+)\)\s*\n".
 -                      "Extra Information: (?P<information>(?:-|[a-zA-Z0-9+/]+={0,2}))\s*\n".
 -                      "Stack Trace: (?P<stack>\[[^\n]+\])", Regex::DOT_ALL);
 -              }
 -              
 -              if (!$regex->match($entry)) {
 -                      throw new \InvalidArgumentException('The given entry is malformed.');
 -              }
 -              $matches = $regex->getMatches();
 -              $chainRegex->match($matches['chain'], true, Regex::ORDER_MATCH_BY_SET);
 -              
 -              $chainMatches = array_map(function ($item) {
 -                      if ($item['information'] === '-') {
 -                              $item['information'] = null;
 -                      }
 -                      else {
 -                              try {
 -                                      $item['information'] = unserialize(base64_decode($item['information']), ['allowed_classes' => false]);
 -                              }
 -                              catch (SystemException $e) {
 -                                      throw new \InvalidArgumentException('The additional information section of the given entry is malformed.', 0, $e);
 -                              }
 -                      }
 -                      
 -                      try {
 -                              $item['stack'] = JSON::decode($item['stack']);
 -                      }
 -                      catch (SystemException $e) {
 -                              throw new \InvalidArgumentException('The stack trace of the given entry is malformed.', 0, $e);
 -                      }
 -                      
 -                      return $item;
 -              }, $chainRegex->getMatches());
 -              
 -              $matches['stackHash'] = sha1(implode("\0", array_map(function ($item) {
 -                      $result = "";
 -                      foreach ($item['stack'] as $stack) {
 -                              $result .= $stack['file']."\t".$stack['line']."\t".$stack['class'].$stack['type'].$stack['function']."\n";
 -                      }
 -                      return $result;
 -              }, $chainMatches)));
 -              
 -              $matches['date'] = strtotime($matches['date']);
 -              $matches['chain'] = $chainMatches;
 -              
 -              return $matches;
 -      }
 -      
 -      /**
 -       * Forbid creation of ExceptionLogUtil objects.
 -       */
 -      private function __construct() {
 -              // does nothing
 -      }
 +final class ExceptionLogUtil
 +{
 +    /**
 +     * Splits the given string of Exceptions into an array.
 +     *
 +     * @param string $contents
 +     * @return  string[]
 +     */
 +    public static function splitLog($contents)
 +    {
 +        // unify newlines
 +        $contents = StringUtil::unifyNewlines($contents);
 +
 +        // split contents
 +        $split = new Regex('(?:^|\n<<<<\n\n)(?:<<<<<<<<([a-f0-9]{40})<<<<\n|$)');
 +        $contents = $split->split($contents, Regex::SPLIT_NON_EMPTY_ONLY | Regex::CAPTURE_SPLIT_DELIMITER);
 +
++        if (empty($contents)) {
++            return [];
++        }
++
 +        // even items become keys, odd items become values
 +        return \array_merge(...\array_map(
 +            static function ($v) {
 +                return [$v[0] => $v[1]];
 +            },
 +            \array_chunk($contents, 2)
 +        ));
 +    }
 +
 +    /**
 +     * Parses the given log entry.
 +     *
 +     * @param string $entry
 +     * @return  mixed[]
 +     */
 +    public static function parseException($entry)
 +    {
 +        static $regex = null;
 +        static $chainRegex = null;
 +        if ($regex === null || $chainRegex === null) {
 +            $regex = new Regex("(?P<date>[MTWFS][a-z]{2}, \\d{1,2} [JFMASOND][a-z]{2} \\d{4} \\d{2}:\\d{2}:\\d{2} [+-]\\d{4})\\s*\n"
 +                . "Message: (?P<message>.*?)\\s*\n"
 +                . "PHP version: (?P<phpVersion>.*?)\\s*\n"
 +                . "WoltLab Suite version: (?P<wcfVersion>.*?)\\s*\n"
 +                . "Request URI: (?P<requestURI>.*?)\\s*\n"
 +                . "Referrer: (?P<referrer>.*?)\\s*\n"
 +                . "User Agent: (?P<userAgent>.*?)\\s*\n"
 +                . "Peak Memory Usage: (?<peakMemory>\\d+)/(?<maxMemory>(?:\\d+|-1))\\s*\n"
 +                . "(?<chain>======\n"
 +                . ".*)", Regex::DOT_ALL);
 +            $chainRegex = new Regex("======\n"
 +                . "Error Class: (?P<class>.*?)\\s*\n"
 +                . "Error Message: (?P<message>.*?)\\s*\n"
 +                . "Error Code: (?P<code>[a-zA-Z0-9]+)\\s*\n"
 +                . "File: (?P<file>.*?) \\((?P<line>\\d+)\\)\\s*\n"
 +                . "Extra Information: (?P<information>(?:-|[a-zA-Z0-9+/]+={0,2}))\\s*\n"
 +                . "Stack Trace: (?P<stack>\\[[^\n]+\\])", Regex::DOT_ALL);
 +        }
 +
 +        if (!$regex->match($entry)) {
 +            throw new \InvalidArgumentException('The given entry is malformed.');
 +        }
 +        $matches = $regex->getMatches();
 +        $chainRegex->match($matches['chain'], true, Regex::ORDER_MATCH_BY_SET);
 +
 +        $chainMatches = \array_map(static function ($item) {
 +            if ($item['information'] === '-') {
 +                $item['information'] = null;
 +            } else {
 +                try {
 +                    $item['information'] = \unserialize(
 +                        \base64_decode($item['information']),
 +                        ['allowed_classes' => false]
 +                    );
 +                } catch (SystemException $e) {
 +                    throw new \InvalidArgumentException(
 +                        'The additional information section of the given entry is malformed.',
 +                        0,
 +                        $e
 +                    );
 +                }
 +            }
 +
 +            try {
 +                $item['stack'] = JSON::decode($item['stack']);
 +            } catch (SystemException $e) {
 +                throw new \InvalidArgumentException('The stack trace of the given entry is malformed.', 0, $e);
 +            }
 +
 +            return $item;
 +        }, $chainRegex->getMatches());
 +
 +        $matches['stackHash'] = \sha1(\implode("\0", \array_map(static function ($item) {
 +            $result = "";
 +            foreach ($item['stack'] as $stack) {
 +                $result .= $stack['file'] . "\t" . $stack['line'] . "\t" . $stack['class'] . $stack['type'] . $stack['function'] . "\n";
 +            }
 +
 +            return $result;
 +        }, $chainMatches)));
 +
 +        $matches['date'] = \strtotime($matches['date']);
 +        $matches['chain'] = $chainMatches;
 +
 +        return $matches;
 +    }
 +
 +    /**
 +     * Forbid creation of ExceptionLogUtil objects.
 +     */
 +    private function __construct()
 +    {
 +        // does nothing
 +    }
  }
Simple merge
Simple merge