Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 57
0.00% covered (danger)
0.00%
0 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
Importer
0.00% covered (danger)
0.00%
0 / 57
0.00% covered (danger)
0.00%
0 / 4
506
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 save
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 generateXML
0.00% covered (danger)
0.00%
0 / 22
0.00% covered (danger)
0.00%
0 / 1
42
 initProcessor
0.00% covered (danger)
0.00%
0 / 29
0.00% covered (danger)
0.00%
0 / 1
182
1<?php
2
3/**
4 * VuFind XSLT importer
5 *
6 * PHP version 8
7 *
8 * Copyright (C) Villanova University 2010.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2,
12 * as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22 *
23 * @category VuFind
24 * @package  XSLT
25 * @author   Demian Katz <demian.katz@villanova.edu>
26 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
27 * @link     https://vufind.org/wiki/ Wiki
28 */
29
30namespace VuFind\XSLT;
31
32use DOMDocument;
33use Laminas\ServiceManager\ServiceLocatorInterface;
34use VuFindSearch\Backend\Solr\Document\RawXMLDocument;
35use XSLTProcessor;
36
37use function is_array;
38
39/**
40 * VuFind XSLT importer
41 *
42 * @category VuFind
43 * @package  XSLT
44 * @author   Demian Katz <demian.katz@villanova.edu>
45 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
46 * @link     https://vufind.org/wiki/ Wiki
47 */
48class Importer
49{
50    /**
51     * Service locator
52     *
53     * @var ServiceLocatorInterface
54     */
55    protected $serviceLocator;
56
57    /**
58     * Constructor
59     *
60     * @param ServiceLocatorInterface $sm Service manager
61     */
62    public function __construct(ServiceLocatorInterface $sm)
63    {
64        $this->serviceLocator = $sm;
65    }
66
67    /**
68     * Save an XML file to the Solr index using the specified configuration.
69     *
70     * @param string $xmlFile    XML file to transform.
71     * @param string $properties Properties file.
72     * @param string $index      Solr index to use.
73     * @param bool   $testMode   Are we in test-only mode?
74     *
75     * @throws \Exception
76     * @return string            Transformed XML
77     */
78    public function save(
79        $xmlFile,
80        $properties,
81        $index = 'Solr',
82        $testMode = false
83    ) {
84        // Process the file:
85        $xml = $this->generateXML($xmlFile, $properties);
86
87        // Save the results (or just display them, if in test mode):
88        if (!$testMode) {
89            $solr = $this->serviceLocator->get(\VuFind\Solr\Writer::class);
90            $solr->save($index, new RawXMLDocument($xml));
91        }
92        return $xml;
93    }
94
95    /**
96     * Transform $xmlFile using the provided $properties configuration.
97     *
98     * @param string $xmlFile    XML file to transform.
99     * @param string $properties Properties file.
100     *
101     * @throws \Exception
102     * @return mixed             Transformed XML.
103     */
104    protected function generateXML($xmlFile, $properties)
105    {
106        // Load properties file:
107        $resolver = $this->serviceLocator->get(\VuFind\Config\PathResolver::class);
108        $properties = $resolver->getConfigPath($properties, 'import');
109        if (!file_exists($properties)) {
110            throw new \Exception("Cannot load properties file: {$properties}.");
111        }
112        $options = parse_ini_file($properties, true);
113
114        // Make sure required parameter is set:
115        if (!($filename = $options['General']['xslt'] ?? '')) {
116            throw new \Exception(
117                "Properties file ({$properties}) is missing General/xslt setting."
118            );
119        }
120        $xslFile = $resolver->getConfigPath($filename, 'import/xsl');
121
122        // Initialize the XSL processor:
123        $xsl = $this->initProcessor($options);
124
125        // Load up the style sheet
126        $style = new DOMDocument();
127        if (!$style->load($xslFile)) {
128            throw new \Exception("Problem loading XSL file: {$xslFile}.");
129        }
130        $xsl->importStyleSheet($style);
131
132        // Load up the XML document
133        $xml = new DOMDocument();
134        if (!$xml->load($xmlFile)) {
135            throw new \Exception("Problem loading XML file: {$xmlFile}.");
136        }
137
138        // Process and return the XML through the style sheet
139        $result = $xsl->transformToXML($xml);
140        if (!$result) {
141            throw new \Exception('Problem transforming XML.');
142        }
143        return $result;
144    }
145
146    /**
147     * Initialize an XSLT processor using settings from the user-specified properties
148     * file.
149     *
150     * @param array $options Parsed contents of properties file.
151     *
152     * @throws \Exception
153     * @return object        XSLT processor.
154     */
155    protected function initProcessor($options)
156    {
157        // Prepare an XSLT processor and pass it some variables
158        $xsl = new XSLTProcessor();
159
160        // Register PHP functions, if specified:
161        if (isset($options['General']['php_function'])) {
162            $functions = is_array($options['General']['php_function'])
163                ? $options['General']['php_function']
164                : [$options['General']['php_function']];
165            foreach ($functions as $function) {
166                $xsl->registerPHPFunctions($function);
167            }
168        }
169
170        // Register custom classes, if specified:
171        if (isset($options['General']['custom_class'])) {
172            $classes = is_array($options['General']['custom_class'])
173                ? $options['General']['custom_class']
174                : [$options['General']['custom_class']];
175            $truncate = $options['General']['truncate_custom_class'] ?? true;
176            foreach ($classes as $class) {
177                // Add a default namespace if none was provided:
178                if (!str_contains($class, '\\')) {
179                    $class = 'VuFind\XSLT\Import\\' . $class;
180                }
181                // If necessary, dynamically generate the truncated version of the
182                // requested class:
183                if ($truncate) {
184                    $parts = explode('\\', $class);
185                    $class = preg_replace('/[^A-Za-z0-9_]/', '', array_pop($parts));
186                    $ns = implode('\\', $parts);
187                    if (!class_exists($class)) {
188                        class_alias("$ns\\$class", $class);
189                    }
190                }
191                $methods = get_class_methods($class);
192                if (method_exists($class, 'setServiceLocator')) {
193                    $class::setServiceLocator($this->serviceLocator);
194                }
195                foreach ($methods as $method) {
196                    $xsl->registerPHPFunctions($class . '::' . $method);
197                }
198            }
199        }
200
201        // Load parameters, if provided:
202        if (isset($options['Parameters'])) {
203            $xsl->setParameter('', $options['Parameters']);
204        }
205
206        return $xsl;
207    }
208}