Passed
Push — master ( 7d59ae...ea2796 )
by Aimeos
04:11
created

Standard::run()   B

Complexity

Conditions 8
Paths 55

Size

Total Lines 70
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 8
eloc 28
c 1
b 0
f 0
nc 55
nop 0
dl 0
loc 70
rs 8.4444

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
5
 * @copyright Aimeos (aimeos.org), 2019-2020
6
 * @package Controller
7
 * @subpackage Jobs
8
 */
9
10
11
namespace Aimeos\Controller\Jobs\Customer\Group\Import\Xml;
12
13
14
/**
15
 * Job controller for XML customer imports
16
 *
17
 * @package Controller
18
 * @subpackage Jobs
19
 */
20
class Standard
21
	extends \Aimeos\Controller\Jobs\Base
22
	implements \Aimeos\Controller\Jobs\Iface
23
{
24
	use \Aimeos\Controller\Common\Common\Import\Xml\Traits;
25
26
27
	/**
28
	 * Returns the localized name of the job.
29
	 *
30
	 * @return string Name of the job
31
	 */
32
	public function getName() : string
33
	{
34
		return $this->getContext()->getI18n()->dt( 'controller/jobs', 'Customer group import XML' );
35
	}
36
37
38
	/**
39
	 * Returns the localized description of the job.
40
	 *
41
	 * @return string Description of the job
42
	 */
43
	public function getDescription() : string
44
	{
45
		return $this->getContext()->getI18n()->dt( 'controller/jobs', 'Imports new and updates existing customer groups from XML files' );
46
	}
47
48
49
	/**
50
	 * Executes the job.
51
	 *
52
	 * @throws \Aimeos\Controller\Jobs\Exception If an error occurs
53
	 */
54
	public function run()
55
	{
56
		$context = $this->getContext();
57
		$config = $context->getConfig();
58
		$logger = $context->getLogger();
59
60
		/** controller/jobs/customer/group/import/xml/location
61
		 * File or directory where the content is stored which should be imported
62
		 *
63
		 * You need to configure the XML file or directory with the XML files that
64
		 * should be imported. It should be an absolute path to be sure but can be
65
		 * relative path if you absolutely know from where the job will be executed
66
		 * from.
67
		 *
68
		 * @param string Absolute file or directory path
69
		 * @since 2019.04
70
		 * @category Developer
71
		 * @category User
72
		 * @see controller/jobs/customer/group/import/xml/container/type
73
		 * @see controller/jobs/customer/group/import/xml/container/content
74
		 * @see controller/jobs/customer/group/import/xml/container/options
75
		 */
76
		$location = $config->get( 'controller/jobs/customer/group/import/xml/location' );
77
78
		try
79
		{
80
			$logger->log( sprintf( 'Started customer group import from "%1$s"', $location ), \Aimeos\MW\Logger\Base::INFO );
81
82
			if( !file_exists( $location ) )
83
			{
84
				$msg = sprintf( 'File or directory "%1$s" doesn\'t exist', $location );
85
				throw new \Aimeos\Controller\Jobs\Exception( $msg );
86
			}
87
88
			$files = [];
89
90
			if( is_dir( $location ) )
91
			{
92
				foreach( new \DirectoryIterator( $location ) as $entry )
93
				{
94
					if( strncmp( $entry->getFilename(), 'customergroup', 8 ) === 0 && $entry->getExtension() === 'xml' ) {
95
						$files[] = $entry->getPathname();
96
					}
97
				}
98
			}
99
			else
100
			{
101
				$files[] = $location;
102
			}
103
104
			sort( $files );
105
			$context->__sleep();
106
107
			$fcn = function( $filepath ) {
108
				$this->import( $filepath );
109
			};
110
111
			foreach( $files as $filepath ) {
112
				$context->getProcess()->start( $fcn, [$filepath] );
113
			}
114
115
			$context->getProcess()->wait();
116
117
			$logger->log( sprintf( 'Finished customer group import from "%1$s"', $location ), \Aimeos\MW\Logger\Base::INFO );
118
		}
119
		catch( \Exception $e )
120
		{
121
			$logger->log( 'Customer group import error: ' . $e->getMessage() . "\n" . $e->getTraceAsString() );
122
			$this->mail( 'Customer group XML import error', $e->getMessage() . "\n" . $e->getTraceAsString() );
123
			throw $e;
124
		}
125
	}
126
127
128
	/**
129
	 * Imports the XML file given by its path
130
	 *
131
	 * @param string $filename Absolute or relative path to the XML file
132
	 */
133
	protected function import( string $filename )
134
	{
135
		$context = $this->getContext();
136
		$config = $context->getConfig();
137
		$logger = $context->getLogger();
138
139
140
		/** controller/jobs/customer/group/import/xml/backup
141
		 * Name of the backup for sucessfully imported files
142
		 *
143
		 * After a XML file was imported successfully, you can move it to another
144
		 * location, so it won't be imported again and isn't overwritten by the
145
		 * next file that is stored at the same location in the file system.
146
		 *
147
		 * You should use an absolute path to be sure but can be relative path
148
		 * if you absolutely know from where the job will be executed from. The
149
		 * name of the new backup location can contain placeholders understood
150
		 * by the PHP strftime() function to create dynamic paths, e.g. "backup/%Y-%m-%d"
151
		 * which would create "backup/2000-01-01". For more information about the
152
		 * strftime() placeholders, please have a look into the PHP documentation of
153
		 * the {@link http://php.net/manual/en/function.strftime.php strftime() function}.
154
		 *
155
		 * **Note:** If no backup name is configured, the file or directory
156
		 * won't be moved away. Please make also sure that the parent directory
157
		 * and the new directory are writable so the file or directory could be
158
		 * moved.
159
		 *
160
		 * @param integer Name of the backup file, optionally with date/time placeholders
161
		 * @since 2019.04
162
		 * @category Developer
163
		 * @see controller/jobs/customer/group/import/xml/domains
164
		 * @see controller/jobs/customer/group/import/xml/max-query
165
		 */
166
		$backup = $config->get( 'controller/jobs/customer/group/import/xml/backup' );
167
168
		/** controller/jobs/customer/group/import/xml/max-query
169
		 * Maximum number of XML nodes processed at once
170
		 *
171
		 * Processing and fetching several customer group items at once speeds up importing
172
		 * the XML files. The more items can be processed at once, the faster the
173
		 * import. More items also increases the memory usage of the importer and
174
		 * thus, this parameter should be low enough to avoid reaching the memory
175
		 * limit of the PHP process.
176
		 *
177
		 * @param integer Number of XML nodes
178
		 * @since 2019.04
179
		 * @category Developer
180
		 * @category User
181
		 * @see controller/jobs/customer/group/import/xml/domains
182
		 * @see controller/jobs/customer/group/import/xml/backup
183
		 */
184
		$maxquery = $config->get( 'controller/jobs/customer/group/import/xml/max-query', 1000 );
185
186
187
		$slice = 0;
188
		$nodes = [];
189
		$xml = new \XMLReader();
190
191
		if( $xml->open( $filename, LIBXML_COMPACT | LIBXML_PARSEHUGE ) === false ) {
192
			throw new \Aimeos\Controller\Jobs\Exception( sprintf( 'No XML file "%1$s" found', $filename ) );
193
		}
194
195
		$logger->log( sprintf( 'Started customer group import from file "%1$s"', $filename ), \Aimeos\MW\Logger\Base::INFO );
196
197
		while( $xml->read() === true )
198
		{
199
			if( $xml->depth === 1 && $xml->nodeType === \XMLReader::ELEMENT && $xml->name === 'customergroupitem' )
200
			{
201
				if( ( $dom = $xml->expand() ) === false )
202
				{
203
					$msg = sprintf( 'Expanding "%1$s" node failed', 'customergroupitem' );
204
					throw new \Aimeos\Controller\Jobs\Exception( $msg );
205
				}
206
207
				$nodes[] = $dom;
208
209
				if( $slice++ >= $maxquery )
210
				{
211
					$this->importNodes( $nodes, [] );
212
					unset( $nodes );
213
					$nodes = [];
214
					$slice = 0;
215
				}
216
			}
217
		}
218
219
		$this->importNodes( $nodes, [] );
220
		unset( $nodes );
221
222
		foreach( $this->getProcessors() as $proc ) {
223
			$proc->finish();
224
		}
225
226
		$logger->log( sprintf( 'Finished customer group import from file "%1$s"', $filename ), \Aimeos\MW\Logger\Base::INFO );
227
228
		if( !empty( $backup ) && @rename( $filename, strftime( $backup ) ) === false )
229
		{
230
			$msg = sprintf( 'Unable to move imported file "%1$s" to "%2$s"', $filename, strftime( $backup ) );
231
			throw new \Aimeos\Controller\Jobs\Exception( $msg );
232
		}
233
	}
234
235
236
	/**
237
	 * Imports the given DOM nodes
238
	 *
239
	 * @param \DomElement[] $nodes List of nodes to import
240
	 * @param string[] $ref List of domain names whose referenced items will be updated in the customer group items
241
	 */
242
	protected function importNodes( array $nodes, array $ref )
243
	{
244
		$codes = $map = [];
245
246
		foreach( $nodes as $node )
247
		{
248
			if( ( $attr = $node->attributes->getNamedItem( 'ref' ) ) !== null ) {
249
				$codes[$attr->nodeValue] = null;
250
			}
251
		}
252
253
		$manager = \Aimeos\MShop::create( $this->getContext(), 'customer/group' );
254
		$search = $manager->createSearch()->setSlice( 0, count( $codes ) );
255
		$search->setConditions( $search->compare( '==', 'customer.group.code', array_keys( $codes ) ) );
256
257
		foreach( $manager->search( $search, $ref ) as $item ) {
258
			$map[$item->getCode()] = $item;
259
		}
260
261
		foreach( $nodes as $node )
262
		{
263
			if( ( $attr = $node->attributes->getNamedItem( 'ref' ) ) !== null && isset( $map[$attr->nodeValue] ) ) {
264
				$item = $this->process( $map[$attr->nodeValue], $node );
265
			} else {
266
				$item = $this->process( $manager->createItem(), $node );
267
			}
268
269
			$manager->saveItem( $item );
0 ignored issues
show
Bug introduced by
The method saveItem() does not exist on Aimeos\MShop\Common\Manager\Iface. Did you maybe mean saveItems()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

269
			$manager->/** @scrutinizer ignore-call */ 
270
             saveItem( $item );

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
270
		}
271
	}
272
273
274
	/**
275
	 * Updates the customer group item and its referenced items using the given DOM node
276
	 *
277
	 * @param \Aimeos\MShop\Customer\Item\Group\Iface $item Customer group item object to update
278
	 * @param \DomElement $node DOM node used for updating the customer group item
279
	 * @return \Aimeos\MShop\Customer\Item\Group\Iface $item Updated customer group item object
280
	 */
281
	protected function process( \Aimeos\MShop\Customer\Item\Group\Iface $item, \DomElement $node ) : \Aimeos\MShop\Customer\Item\Group\Iface
282
	{
283
		$list = [];
284
285
		foreach( $node->attributes as $attr ) {
286
			$list[$attr->nodeName] = $attr->nodeValue;
287
		}
288
289
		foreach( $node->childNodes as $tag ) {
290
			$list[$tag->nodeName] = $tag->nodeValue;
291
		}
292
293
		return $item->fromArray( $list, true );
294
	}
295
}
296