1<?php
2/**
3 * Joomla! Content Management System
4 *
5 * @copyright  Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved.
6 * @license    GNU General Public License version 2 or later; see LICENSE.txt
7 */
8
9namespace Joomla\CMS\Table\Observer;
10
11defined('JPATH_PLATFORM') or die;
12
13/**
14 * Abstract class defining methods that can be
15 * implemented by an Observer class of a Table class (which is an Observable).
16 * Attaches $this Observer to the $table in the constructor.
17 * The classes extending this class should not be instantiated directly, as they
18 * are automatically instanciated by the \JObserverMapper
19 *
20 * @since  3.1.2
21 */
22class Tags extends AbstractObserver
23{
24	/**
25	 * Helper object for managing tags
26	 *
27	 * @var    \JHelperTags
28	 * @since  3.1.2
29	 */
30	protected $tagsHelper;
31
32	/**
33	 * The pattern for this table's TypeAlias
34	 *
35	 * @var    string
36	 * @since  3.1.2
37	 */
38	protected $typeAliasPattern = null;
39
40	/**
41	 * Override for postStoreProcess param newTags, Set by setNewTags, used by onAfterStore and onBeforeStore
42	 *
43	 * @var    array
44	 * @since  3.1.2
45	 */
46	protected $newTags = false;
47
48	/**
49	 * Override for postStoreProcess param replaceTags. Set by setNewTags, used by onAfterStore
50	 *
51	 * @var    boolean
52	 * @since  3.1.2
53	 */
54	protected $replaceTags = true;
55
56	/**
57	 * Not public, so marking private and deprecated, but needed internally in parseTypeAlias for
58	 * PHP < 5.4.0 as it's not passing context $this to closure function.
59	 *
60	 * @var         Tags
61	 * @since       3.1.2
62	 * @deprecated  Never use this
63	 * @private
64	 */
65	public static $_myTableForPregreplaceOnly;
66
67	/**
68	 * Creates the associated observer instance and attaches it to the $observableObject
69	 * Creates the associated tags helper class instance
70	 * $typeAlias can be of the form "{variableName}.type", automatically replacing {variableName} with table-instance variables variableName
71	 *
72	 * @param   \JObservableInterface  $observableObject  The subject object to be observed
73	 * @param   array                  $params            ( 'typeAlias' => $typeAlias )
74	 *
75	 * @return  Tags
76	 *
77	 * @since   3.1.2
78	 */
79	public static function createObserver(\JObservableInterface $observableObject, $params = array())
80	{
81		$typeAlias = $params['typeAlias'];
82
83		$observer = new self($observableObject);
84
85		$observer->tagsHelper = new \JHelperTags;
86		$observer->typeAliasPattern = $typeAlias;
87
88		return $observer;
89	}
90
91	/**
92	 * Pre-processor for $table->store($updateNulls)
93	 *
94	 * @param   boolean  $updateNulls  The result of the load
95	 * @param   string   $tableKey     The key of the table
96	 *
97	 * @return  void
98	 *
99	 * @since   3.1.2
100	 */
101	public function onBeforeStore($updateNulls, $tableKey)
102	{
103		$this->parseTypeAlias();
104
105		if (empty($this->table->tagsHelper->tags))
106		{
107			$this->tagsHelper->preStoreProcess($this->table);
108		}
109		else
110		{
111			$this->tagsHelper->preStoreProcess($this->table, (array) $this->table->tagsHelper->tags);
112		}
113	}
114
115	/**
116	 * Post-processor for $table->store($updateNulls)
117	 * You can change optional params newTags and replaceTags of tagsHelper with method setNewTagsToAdd
118	 *
119	 * @param   boolean  &$result  The result of the load
120	 *
121	 * @return  void
122	 *
123	 * @since   3.1.2
124	 */
125	public function onAfterStore(&$result)
126	{
127		if ($result)
128		{
129			if (empty($this->table->tagsHelper->tags))
130			{
131				$result = $this->tagsHelper->postStoreProcess($this->table);
132			}
133			else
134			{
135				$result = $this->tagsHelper->postStoreProcess($this->table, $this->table->tagsHelper->tags);
136			}
137
138			// Restore default values for the optional params:
139			$this->newTags = array();
140			$this->replaceTags = true;
141		}
142	}
143
144	/**
145	 * Pre-processor for $table->delete($pk)
146	 *
147	 * @param   mixed  $pk  An optional primary key value to delete.  If not set the instance property value is used.
148	 *
149	 * @return  void
150	 *
151	 * @since   3.1.2
152	 * @throws  \UnexpectedValueException
153	 */
154	public function onBeforeDelete($pk)
155	{
156		$this->parseTypeAlias();
157		$this->tagsHelper->deleteTagData($this->table, $pk);
158	}
159
160	/**
161	 * Sets the new tags to be added or to replace existing tags
162	 *
163	 * @param   array    $newTags      New tags to be added to or replace current tags for an item
164	 * @param   boolean  $replaceTags  Replace tags (true) or add them (false)
165	 *
166	 * @return  boolean
167	 *
168	 * @since   3.1.2
169	 */
170	public function setNewTags($newTags, $replaceTags)
171	{
172		$this->parseTypeAlias();
173
174		return $this->tagsHelper->postStoreProcess($this->table, $newTags, $replaceTags);
175	}
176
177	/**
178	 * Internal method
179	 * Parses a TypeAlias of the form "{variableName}.type", replacing {variableName} with table-instance variables variableName
180	 * Storing result into $this->tagsHelper->typeAlias
181	 *
182	 * @return  void
183	 *
184	 * @since   3.1.2
185	 */
186	protected function parseTypeAlias()
187	{
188		// Needed for PHP < 5.4.0 as it's not passing context $this to closure function
189		static::$_myTableForPregreplaceOnly = $this->table;
190
191		$this->tagsHelper->typeAlias = preg_replace_callback('/{([^}]+)}/',
192			function($matches)
193			{
194				return Tags::$_myTableForPregreplaceOnly->{$matches[1]};
195			},
196			$this->typeAliasPattern
197		);
198	}
199}
200