Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
93.88% covered (success)
93.88%
46 / 49
80.00% covered (warning)
80.00%
4 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
PageList
93.88% covered (success)
93.88%
46 / 49
80.00% covered (warning)
80.00%
4 / 5
31.22
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getNumber
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 buildNumber
100.00% covered (success)
100.00%
31 / 31
100.00% covered (success)
100.00%
1 / 1
15
 numberInRange
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
10
 getRangeStart
57.14% covered (warning)
57.14%
4 / 7
0.00% covered (danger)
0.00%
0 / 1
3.71
1<?php
2
3namespace ProofreadPage\Pagination;
4
5use RuntimeException;
6
7/**
8 * @license GPL-2.0-or-later
9 *
10 * Representation for a <pagelist> tag
11 */
12class PageList {
13
14    /**
15     * @var array parameters of the <pagelist> tag
16     */
17    private $params;
18
19    /**
20     * @var PageNumber[] PageNumber already computed
21     */
22    private $pageNumbers = [];
23
24    /**
25     * @param array $params parameters of the <pagelist> tag
26     */
27    public function __construct( array $params ) {
28        $this->params = $params;
29    }
30
31    /**
32     * Returns the PageNumber for the page number $pageNumber
33     *
34     * @param int $pageNumber
35     * @return PageNumber
36     */
37    public function getNumber( int $pageNumber ): PageNumber {
38        if ( !array_key_exists( $pageNumber, $this->pageNumbers ) ) {
39            $this->pageNumbers[$pageNumber] = $this->buildNumber( $pageNumber );
40        }
41
42        return $this->pageNumbers[$pageNumber];
43    }
44
45    /**
46     * Returns the PageNumber for the page number $pageNumber
47     *
48     * @param int $pageNumber
49     * @return PageNumber
50     */
51    private function buildNumber( int $pageNumber ): PageNumber {
52        // default mode
53        $mode = PageNumber::DISPLAY_NORMAL;
54        $offset = 0;
55        $displayedPageNumber = '';
56        $isEmpty = false;
57        $isRecto = true;
58
59        foreach ( $this->params as $num => $parameters ) {
60            if ( is_numeric( $num ) && $num <= $pageNumber ) {
61                $params = explode( ';', $parameters );
62                foreach ( $params as $param ) {
63                    if ( is_numeric( $param ) ) {
64                        $offset = $num - (int)$param;
65                    }
66                }
67            }
68
69            if ( $this->numberInRange( $num, $pageNumber ) ) {
70                $params = explode( ';', $parameters );
71                foreach ( $params as $param ) {
72                    if ( !is_numeric( $param ) ) {
73                        if ( in_array( $param, PageNumber::getDisplayModes(), true ) ) {
74                            $mode = $param;
75                        } elseif ( $param == PageNumber::DISPLAY_EMPTY ) {
76                            $isEmpty = true;
77                        } else {
78                            $displayedPageNumber = $param;
79                        }
80                    }
81                }
82
83                if ( $mode == PageNumber::DISPLAY_FOLIO
84                    || $mode == PageNumber::DISPLAY_FOLIOHIGHROMAN
85                    || $mode == PageNumber::DISPLAY_FOLIOROMAN ) {
86                    $folioStart = $this->getRangeStart( $num );
87                    $displayedPageNumber = (int)$folioStart - $offset
88                        + (int)( ( $pageNumber - (int)$folioStart ) / 2 );
89
90                    $isRecto = ( $pageNumber - (int)$folioStart ) % 2 === 0;
91                }
92            }
93        }
94
95        $displayedPageNumber = ( $displayedPageNumber === '' )
96            ? $pageNumber - $offset
97            : $displayedPageNumber;
98        return new PageNumber( $displayedPageNumber, $mode, $isEmpty, $isRecto );
99    }
100
101    /**
102     * Returns if a number is in a range as defined by <pagelist>
103     * Such range may be a single integer or something in the format XXtoYY
104     *
105     * @param string $range
106     * @param int $number
107     * @return bool
108     */
109    protected function numberInRange( string $range, int $number ): bool {
110        return ( is_numeric( $range ) && $range == $number ) ||
111            ( preg_match( '/^([0-9]*)to([0-9]*)((even|odd)?)$/', $range, $m ) &&
112            $m[1] <= $number && $number <= $m[2] &&
113                ( $m[3] === ''
114                || ( $m[3] === 'even' && $number % 2 === 0 )
115                || ( $m[3] === 'odd' && $number % 2 === 1 ) )
116            );
117    }
118
119    /**
120     * @param string $range
121     * @return string
122     * @throws RuntimeException
123     */
124    private function getRangeStart( string $range ): string {
125        if ( is_numeric( $range ) ) {
126            return $range;
127        } elseif ( preg_match( '/^([0-9]*)to([0-9]*)((even|odd)?)$/', $range, $m ) ) {
128            return $m[1];
129        } else {
130            throw new RuntimeException(
131                $range . ' is not a valid range.'
132            );
133        }
134    }
135}