CiteProc::citation()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 2
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
/*
3
 * citeproc-php
4
 *
5
 * @link        http://github.com/seboettg/citeproc-php for the source repository
6
 * @copyright   Copyright (c) 2016 Sebastian Böttger.
7
 * @license     https://opensource.org/licenses/MIT
8
 */
9
10
namespace Seboettg\CiteProc;
11
12
use Exception;
13
use InvalidArgumentException;
14
use Seboettg\CiteProc\Data\DataList;
15
use Seboettg\CiteProc\Exception\CiteProcException;
16
use Seboettg\CiteProc\Root\Info;
17
use Seboettg\CiteProc\Style\Bibliography;
18
use Seboettg\CiteProc\Style\Citation;
19
use Seboettg\CiteProc\Style\Macro;
20
use Seboettg\CiteProc\Style\Options\GlobalOptions;
21
use Seboettg\CiteProc\Root\Root;
22
use Seboettg\CiteProc\Styles\Css\CssStyle;
23
use Seboettg\CiteProc\Util\CiteProcHelper;
24
use Seboettg\Collection\ArrayList;
25
use Seboettg\Collection\Lists\ListInterface;
26
use Seboettg\Collection\Map\MapInterface;
27
use SimpleXMLElement;
28
use function Seboettg\Collection\Lists\listOf;
29
use function Seboettg\Collection\Map\emptyMap;
30
31
/**
32
 * Class CiteProc
33
 * @package Seboettg\CiteProc
34
 *
35
 * @author Sebastian Böttger <[email protected]>
36
 */
37
class CiteProc
38
{
39
40
    /**
41
     * @var Context
42
     */
43
    private static $context;
44
45
46
    /**
47
     * @return Context
48
     */
49
    public static function getContext()
50
    {
51
        return self::$context;
52
    }
53
54
    /**
55
     * @param Context $context
56
     */
57
    public static function setContext($context)
58
    {
59
        self::$context = $context;
60
    }
61
62
    private $lang;
63
64
    /**
65
     * @var string
66
     */
67
    private $styleSheet;
68
69
    /**
70
     * @var SimpleXMLElement
71
     */
72
    private $styleSheetXml;
73
74
    /**
75
     * @var array
76
     */
77
    private $markupExtension;
78
79
    /**
80
     * CiteProc constructor.
81
     * @param string $styleSheet xml formatted csl stylesheet
82
     * @param string $lang
83
     * @param array $markupExtension
84
     */
85
    public function __construct($styleSheet, $lang = "en-US", $markupExtension = [])
86
    {
87
        $this->styleSheet = $styleSheet;
88
        $this->lang = $lang;
89
        $this->markupExtension = $markupExtension;
90
    }
91
92
    public function __destruct()
93
    {
94
        self::$context = null;
95
    }
96
97
    /**
98
     * @param SimpleXMLElement $style
99
     * @throws CiteProcException
100
     */
101
    private function parse(SimpleXMLElement $style)
102
    {
103
        $root = new Root();
104
        $root->initInheritableNameAttributes($style);
105
        self::$context->setRoot($root);
106
        $globalOptions = new GlobalOptions($style);
107
        self::$context->setGlobalOptions($globalOptions);
108
109
        /** @var SimpleXMLElement $node */
110
        foreach ($style as $node) {
111
            $name = $node->getName();
0 ignored issues
show
Bug introduced by
The method getName() does not exist on null. ( Ignorable by Annotation )

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

111
            /** @scrutinizer ignore-call */ 
112
            $name = $node->getName();

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...
112
            switch ($name) {
113
                case 'info':
114
                    self::$context->setInfo(new Info($node));
0 ignored issues
show
Bug introduced by
It seems like $node can also be of type null; however, parameter $node of Seboettg\CiteProc\Root\Info::__construct() does only seem to accept SimpleXMLElement, maybe add an additional type check? ( Ignorable by Annotation )

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

114
                    self::$context->setInfo(new Info(/** @scrutinizer ignore-type */ $node));
Loading history...
115
                    break;
116
                case 'locale':
117
                    self::$context->getLocale()->addXml($node);
0 ignored issues
show
Bug introduced by
It seems like $node can also be of type null; however, parameter $xml of Seboettg\CiteProc\Locale\Locale::addXml() does only seem to accept SimpleXMLElement, maybe add an additional type check? ( Ignorable by Annotation )

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

117
                    self::$context->getLocale()->addXml(/** @scrutinizer ignore-type */ $node);
Loading history...
118
                    break;
119
                case 'macro':
120
                    $macro = new Macro($node, $root);
0 ignored issues
show
Bug introduced by
It seems like $node can also be of type null; however, parameter $node of Seboettg\CiteProc\Style\Macro::__construct() does only seem to accept SimpleXMLElement, maybe add an additional type check? ( Ignorable by Annotation )

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

120
                    $macro = new Macro(/** @scrutinizer ignore-type */ $node, $root);
Loading history...
121
                    self::$context->addMacro($macro->getName(), $macro);
122
                    break;
123
                case 'bibliography':
124
                    $bibliography = new Bibliography($node, $root);
0 ignored issues
show
Bug introduced by
It seems like $node can also be of type null; however, parameter $node of Seboettg\CiteProc\Style\...iography::__construct() does only seem to accept SimpleXMLElement, maybe add an additional type check? ( Ignorable by Annotation )

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

124
                    $bibliography = new Bibliography(/** @scrutinizer ignore-type */ $node, $root);
Loading history...
125
                    self::$context->setBibliography($bibliography);
126
                    break;
127
                case 'citation':
128
                    $citation = new Citation($node, $root);
0 ignored issues
show
Bug introduced by
It seems like $node can also be of type null; however, parameter $node of Seboettg\CiteProc\Style\Citation::__construct() does only seem to accept SimpleXMLElement, maybe add an additional type check? ( Ignorable by Annotation )

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

128
                    $citation = new Citation(/** @scrutinizer ignore-type */ $node, $root);
Loading history...
129
                    self::$context->setCitation($citation);
130
                    break;
131
            }
132
        }
133
    }
134
135
    /**
136
     * @param DataList $data
137
     * @return string
138
     */
139
    protected function bibliography($data)
140
    {
141
142
        return self::$context->getBibliography()->render($data);
143
    }
144
145
    protected function citation(DataList $data, MapInterface $citationItems)
146
    {
147
        return self::$context->getCitation()->render($data, $citationItems);
148
    }
149
150
    /**
151
     * @param array|DataList $data
152
     * @param string $mode (citation|bibliography)
153
     * @param array $citationItems
154
     * @param bool $citationAsArray
155
     * @return string|array
156
     * @throws CiteProcException
157
     */
158
    public function render(
159
        $data_,
160
        string $mode = "bibliography",
161
        array $citationItems = [],
162
        bool $citationAsArray = false
163
    ) {
164
        if (is_array($data_)) {
165
            $data_ = CiteProcHelper::cloneArray($data_);
166
        }
167
168
        if (!in_array($mode, ['citation', 'bibliography'])) {
169
            throw new InvalidArgumentException("\"$mode\" is not a valid mode.");
170
        }
171
172
        $this->init($citationAsArray); //initialize
173
174
175
        if ($data_ instanceof Data\DataList) {
176
            $dataList = $data_;
177
        } else {
178
            if (is_array($data_)) {
179
                $dataList = new DataList();
180
                $dataList->setArray($data_);
181
            } else {
182
                throw new CiteProcException('No valid format for variable data. Either DataList or array expected');
183
            }
184
        }
185
186
        switch ($mode) {
187
            case 'bibliography':
188
                self::$context->setMode($mode);
189
                // set CitationItems to Context
190
                self::getContext()->setCitationData($dataList);
191
                $res = $this->bibliography($dataList);
192
                break;
193
            case 'citation':
194
                if (is_array($citationItems)) {
0 ignored issues
show
introduced by
The condition is_array($citationItems) is always true.
Loading history...
195
                    $citeItems = emptyMap();
196
                    $citationItems = listOfLists(...$citationItems);
197
                    foreach ($data_ as $key => $value) {
198
                        if (property_exists($value, "id") && $citationItems
199
                                ->map(fn ($item) => $item->id)->contains($value->id)) {
200
                            $k = $key + 1;
201
                            $citeItems->put("$k", $value->id);
202
                        }
203
                    }
204
                } elseif (!($citationItems instanceof ListInterface)) {
205
                    throw new CiteProcException('No valid format for variable `citationItems`, ArrayList expected.');
206
                }
207
                self::$context->setMode($mode);
208
                // set CitationItems to Context
209
                self::getContext()->setCitationItems($citationItems);
210
                $res = $this->citation($dataList, $citeItems);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $citeItems does not seem to be defined for all execution paths leading up to this point.
Loading history...
211
                break;
212
            default:
213
                throw new CiteProcException("Invalid mode $mode");
214
        }
215
        self::setContext(null);
216
217
        return $res;
218
    }
219
220
    /**
221
     * initializes CiteProc and start parsing XML stylesheet
222
     * @param bool $citationAsArray
223
     * @throws CiteProcException
224
     * @throws Exception
225
     */
226
    public function init(bool $citationAsArray = false)
227
    {
228
        self::$context = new Context();
229
        self::$context->setLocale(new Locale\Locale($this->lang)); //init locale
230
        self::$context->setCitationsAsArray($citationAsArray);
231
        // set markup extensions
232
        self::$context->setMarkupExtension($this->markupExtension);
233
        $this->styleSheetXml = new SimpleXMLElement($this->styleSheet);
234
        $this->parse($this->styleSheetXml);
235
    }
236
237
    /**
238
     * @return string
239
     * @throws CiteProcException
240
     */
241
    public function renderCssStyles()
242
    {
243
        if (self::getContext() === null) {
244
            $this->init();
245
        }
246
247
        if (self::getContext()->getCssStyle() == null) {
248
            $cssStyle = new CssStyle(self::getContext()->getBibliographySpecificOptions());
249
            self::getContext()->setCssStyle($cssStyle);
250
        }
251
252
        return self::getContext()->getCssStyle()->render();
253
    }
254
}
255