Completed
Push — master ( 6706d8...93737b )
by
unknown
07:43
created

Page::check_page_break()   F

Complexity

Conditions 39
Paths 306

Size

Total Lines 163
Code Lines 92

Duplication

Lines 19
Ratio 11.66 %

Importance

Changes 0
Metric Value
dl 19
loc 163
rs 3.3457
c 0
b 0
f 0
cc 39
eloc 92
nc 306
nop 1

How to fix   Long Method    Complexity   

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
 * @package dompdf
4
 * @link    http://dompdf.github.com/
5
 * @author  Benj Carson <[email protected]>
6
 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
 */
8
namespace Dompdf\FrameDecorator;
9
10
use Dompdf\Css\Style;
11
use Dompdf\Dompdf;
12
use Dompdf\Helpers;
13
use Dompdf\Frame;
14
use Dompdf\FrameDecorator\Table as TableFrameDecorator;
15
use Dompdf\FrameDecorator\TableRow as TableRowFrameDecorator;
16
use Dompdf\Renderer;
17
18
/**
19
 * Decorates frames for page layout
20
 *
21
 * @access  private
22
 * @package dompdf
23
 */
24
class Page extends AbstractFrameDecorator
25
{
26
27
    /**
28
     * y value of bottom page margin
29
     *
30
     * @var float
31
     */
32
    protected $_bottom_page_margin;
33
34
    /**
35
     * Flag indicating page is full.
36
     *
37
     * @var bool
38
     */
39
    protected $_page_full;
40
41
    /**
42
     * Number of tables currently being reflowed
43
     *
44
     * @var int
45
     */
46
    protected $_in_table;
47
48
    /**
49
     * The pdf renderer
50
     *
51
     * @var Renderer
52
     */
53
    protected $_renderer;
54
55
    /**
56
     * This page's floating frames
57
     *
58
     * @var array
59
     */
60
    protected $_floating_frames = array();
61
62
    //........................................................................
0 ignored issues
show
Unused Code Comprehensibility introduced by
100% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
63
64
    /**
65
     * Class constructor
66
     *
67
     * @param Frame $frame the frame to decorate
68
     * @param Dompdf $dompdf
69
     */
70
    function __construct(Frame $frame, Dompdf $dompdf)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
71
    {
72
        parent::__construct($frame, $dompdf);
73
        $this->_page_full = false;
74
        $this->_in_table = 0;
75
        $this->_bottom_page_margin = null;
76
    }
77
78
    /**
79
     * Set the renderer used for this pdf
80
     *
81
     * @param Renderer $renderer the renderer to use
82
     */
83
    function set_renderer($renderer)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
84
    {
85
        $this->_renderer = $renderer;
86
    }
87
88
    /**
89
     * Return the renderer used for this pdf
90
     *
91
     * @return Renderer
92
     */
93
    function get_renderer()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
94
    {
95
        return $this->_renderer;
96
    }
97
98
    /**
99
     * Set the frame's containing block.  Overridden to set $this->_bottom_page_margin.
100
     *
101
     * @param float $x
102
     * @param float $y
103
     * @param float $w
104
     * @param float $h
105
     */
106
    function set_containing_block($x = null, $y = null, $w = null, $h = null)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
107
    {
108
        parent::set_containing_block($x, $y, $w, $h);
109
        //$w = $this->get_containing_block("w");
0 ignored issues
show
Unused Code Comprehensibility introduced by
64% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
110
        if (isset($h)) {
111
            $this->_bottom_page_margin = $h;
112
        } // - $this->_frame->get_style()->length_in_pt($this->_frame->get_style()->margin_bottom, $w);
0 ignored issues
show
Unused Code Comprehensibility introduced by
63% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
113
    }
114
115
    /**
116
     * Returns true if the page is full and is no longer accepting frames.
117
     *
118
     * @return bool
119
     */
120
    function is_full()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
121
    {
122
        return $this->_page_full;
123
    }
124
125
    /**
126
     * Start a new page by resetting the full flag.
127
     */
128
    function next_page()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
129
    {
130
        $this->_floating_frames = array();
131
        $this->_renderer->new_page();
132
        $this->_page_full = false;
133
    }
134
135
    /**
136
     * Indicate to the page that a table is currently being reflowed.
137
     */
138
    function table_reflow_start()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
139
    {
140
        $this->_in_table++;
141
    }
142
143
    /**
144
     * Indicate to the page that table reflow is finished.
145
     */
146
    function table_reflow_end()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
147
    {
148
        $this->_in_table--;
149
    }
150
151
    /**
152
     * Return whether we are currently in a nested table or not
153
     *
154
     * @return bool
155
     */
156
    function in_nested_table()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
157
    {
158
        return $this->_in_table > 1;
159
    }
160
161
    /**
162
     * Check if a forced page break is required before $frame.  This uses the
163
     * frame's page_break_before property as well as the preceeding frame's
164
     * page_break_after property.
165
     *
166
     * @link http://www.w3.org/TR/CSS21/page.html#forced
167
     *
168
     * @param Frame $frame the frame to check
169
     *
170
     * @return bool true if a page break occured
171
     */
172
    function check_forced_page_break(Frame $frame)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
173
    {
174
175
        // Skip check if page is already split
176
        if ($this->_page_full) {
177
            return null;
178
        }
179
180
        $block_types = array("block", "list-item", "table", "inline");
181
        $page_breaks = array("always", "left", "right");
182
183
        $style = $frame->get_style();
184
185
        if (!in_array($style->display, $block_types)) {
0 ignored issues
show
Documentation introduced by
The property display does not exist on object<Dompdf\Css\Style>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
186
            return false;
187
        }
188
189
        // Find the previous block-level sibling
190
        $prev = $frame->get_prev_sibling();
191
192
        while ($prev && !in_array($prev->get_style()->display, $block_types)) {
0 ignored issues
show
Documentation introduced by
The property display does not exist on object<Dompdf\Css\Style>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
193
            $prev = $prev->get_prev_sibling();
194
        }
195
196 View Code Duplication
        if (in_array($style->page_break_before, $page_breaks)) {
0 ignored issues
show
Documentation introduced by
The property page_break_before does not exist on object<Dompdf\Css\Style>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
197
            // Prevent cascading splits
198
            $frame->split(null, true);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Dompdf\Frame as the method split() does only exist in the following sub-classes of Dompdf\Frame: Dompdf\FrameDecorator\AbstractFrameDecorator, Dompdf\FrameDecorator\Block, Dompdf\FrameDecorator\Image, Dompdf\FrameDecorator\Inline, Dompdf\FrameDecorator\ListBullet, Dompdf\FrameDecorator\ListBulletImage, Dompdf\FrameDecorator\NullFrameDecorator, Dompdf\FrameDecorator\Page, Dompdf\FrameDecorator\Table, Dompdf\FrameDecorator\TableCell, Dompdf\FrameDecorator\TableRow, Dompdf\FrameDecorator\TableRowGroup, Dompdf\FrameDecorator\Text. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
199
            // We have to grab the style again here because split() resets
200
            // $frame->style to the frame's orignal style.
201
            $frame->get_style()->page_break_before = "auto";
0 ignored issues
show
Documentation introduced by
The property page_break_before does not exist on object<Dompdf\Css\Style>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
202
            $this->_page_full = true;
203
204
            return true;
205
        }
206
207 View Code Duplication
        if ($prev && in_array($prev->get_style()->page_break_after, $page_breaks)) {
0 ignored issues
show
Documentation introduced by
The property page_break_after does not exist on object<Dompdf\Css\Style>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
208
            // Prevent cascading splits
209
            $frame->split(null, true);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Dompdf\Frame as the method split() does only exist in the following sub-classes of Dompdf\Frame: Dompdf\FrameDecorator\AbstractFrameDecorator, Dompdf\FrameDecorator\Block, Dompdf\FrameDecorator\Image, Dompdf\FrameDecorator\Inline, Dompdf\FrameDecorator\ListBullet, Dompdf\FrameDecorator\ListBulletImage, Dompdf\FrameDecorator\NullFrameDecorator, Dompdf\FrameDecorator\Page, Dompdf\FrameDecorator\Table, Dompdf\FrameDecorator\TableCell, Dompdf\FrameDecorator\TableRow, Dompdf\FrameDecorator\TableRowGroup, Dompdf\FrameDecorator\Text. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
210
            $prev->get_style()->page_break_after = "auto";
0 ignored issues
show
Documentation introduced by
The property page_break_after does not exist on object<Dompdf\Css\Style>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
211
            $this->_page_full = true;
212
213
            return true;
214
        }
215
216
        if ($prev && $prev->get_last_child() && $frame->get_node()->nodeName != "body") {
217
            $prev_last_child = $prev->get_last_child();
218 View Code Duplication
            if (in_array($prev_last_child->get_style()->page_break_after, $page_breaks)) {
0 ignored issues
show
Documentation introduced by
The property page_break_after does not exist on object<Dompdf\Css\Style>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
219
                $frame->split(null, true);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Dompdf\Frame as the method split() does only exist in the following sub-classes of Dompdf\Frame: Dompdf\FrameDecorator\AbstractFrameDecorator, Dompdf\FrameDecorator\Block, Dompdf\FrameDecorator\Image, Dompdf\FrameDecorator\Inline, Dompdf\FrameDecorator\ListBullet, Dompdf\FrameDecorator\ListBulletImage, Dompdf\FrameDecorator\NullFrameDecorator, Dompdf\FrameDecorator\Page, Dompdf\FrameDecorator\Table, Dompdf\FrameDecorator\TableCell, Dompdf\FrameDecorator\TableRow, Dompdf\FrameDecorator\TableRowGroup, Dompdf\FrameDecorator\Text. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
220
                $prev_last_child->get_style()->page_break_after = "auto";
0 ignored issues
show
Documentation introduced by
The property page_break_after does not exist on object<Dompdf\Css\Style>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
221
                $this->_page_full = true;
222
223
                return true;
224
            }
225
        }
226
227
        return false;
228
    }
229
230
    /**
231
     * Determine if a page break is allowed before $frame
232
     * http://www.w3.org/TR/CSS21/page.html#allowed-page-breaks
233
     *
234
     * In the normal flow, page breaks can occur at the following places:
235
     *
236
     *    1. In the vertical margin between block boxes. When a page
237
     *    break occurs here, the used values of the relevant
238
     *    'margin-top' and 'margin-bottom' properties are set to '0'.
239
     *    2. Between line boxes inside a block box.
240
     *
241
     * These breaks are subject to the following rules:
242
     *
243
     *   * Rule A: Breaking at (1) is allowed only if the
244
     *     'page-break-after' and 'page-break-before' properties of
245
     *     all the elements generating boxes that meet at this margin
246
     *     allow it, which is when at least one of them has the value
247
     *     'always', 'left', or 'right', or when all of them are
248
     *     'auto'.
249
     *
250
     *   * Rule B: However, if all of them are 'auto' and the
251
     *     nearest common ancestor of all the elements has a
252
     *     'page-break-inside' value of 'avoid', then breaking here is
253
     *     not allowed.
254
     *
255
     *   * Rule C: Breaking at (2) is allowed only if the number of
256
     *     line boxes between the break and the start of the enclosing
257
     *     block box is the value of 'orphans' or more, and the number
258
     *     of line boxes between the break and the end of the box is
259
     *     the value of 'widows' or more.
260
     *
261
     *   * Rule D: In addition, breaking at (2) is allowed only if
262
     *     the 'page-break-inside' property is 'auto'.
263
     *
264
     * If the above doesn't provide enough break points to keep
265
     * content from overflowing the page boxes, then rules B and D are
266
     * dropped in order to find additional breakpoints.
267
     *
268
     * If that still does not lead to sufficient break points, rules A
269
     * and C are dropped as well, to find still more break points.
270
     *
271
     * We will also allow breaks between table rows.  However, when
272
     * splitting a table, the table headers should carry over to the
273
     * next page (but they don't yet).
274
     *
275
     * @param Frame $frame the frame to check
276
     *
277
     * @return bool true if a break is allowed, false otherwise
278
     */
279
    protected function _page_break_allowed(Frame $frame)
280
    {
281
        $block_types = array("block", "list-item", "table", "-dompdf-image");
282
        Helpers::dompdf_debug("page-break", "_page_break_allowed(" . $frame->get_node()->nodeName . ")");
283
        $display = $frame->get_style()->display;
0 ignored issues
show
Documentation introduced by
The property display does not exist on object<Dompdf\Css\Style>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
284
285
        // Block Frames (1):
0 ignored issues
show
Unused Code Comprehensibility introduced by
45% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
286
        if (in_array($display, $block_types)) {
287
288
            // Avoid breaks within table-cells
289
            if ($this->_in_table) {
290
                Helpers::dompdf_debug("page-break", "In table: " . $this->_in_table);
291
292
                return false;
293
            }
294
295
            // Rules A & B
296
297
            if ($frame->get_style()->page_break_before === "avoid") {
0 ignored issues
show
Documentation introduced by
The property page_break_before does not exist on object<Dompdf\Css\Style>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
298
                Helpers::dompdf_debug("page-break", "before: avoid");
299
300
                return false;
301
            }
302
303
            // Find the preceeding block-level sibling
304
            $prev = $frame->get_prev_sibling();
305
            while ($prev && !in_array($prev->get_style()->display, $block_types)) {
0 ignored issues
show
Documentation introduced by
The property display does not exist on object<Dompdf\Css\Style>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
306
                $prev = $prev->get_prev_sibling();
307
            }
308
309
            // Does the previous element allow a page break after?
310
            if ($prev && $prev->get_style()->page_break_after === "avoid") {
0 ignored issues
show
Documentation introduced by
The property page_break_after does not exist on object<Dompdf\Css\Style>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
311
                Helpers::dompdf_debug("page-break", "after: avoid");
312
313
                return false;
314
            }
315
316
            // If both $prev & $frame have the same parent, check the parent's
317
            // page_break_inside property.
318
            $parent = $frame->get_parent();
319
            if ($prev && $parent && $parent->get_style()->page_break_inside === "avoid") {
0 ignored issues
show
Documentation introduced by
The property page_break_inside does not exist on object<Dompdf\Css\Style>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
320
                Helpers::dompdf_debug("page-break", "parent inside: avoid");
321
322
                return false;
323
            }
324
325
            // To prevent cascading page breaks when a top-level element has
326
            // page-break-inside: avoid, ensure that at least one frame is
327
            // on the page before splitting.
328 View Code Duplication
            if ($parent->get_node()->nodeName === "body" && !$prev) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
329
                // We are the body's first child
330
                Helpers::dompdf_debug("page-break", "Body's first child.");
331
332
                return false;
333
            }
334
335
            // If the frame is the first block-level frame, use the value from
336
            // $frame's parent instead.
337
            if (!$prev && $parent) {
338
                return $this->_page_break_allowed($parent);
339
            }
340
341
            Helpers::dompdf_debug("page-break", "block: break allowed");
342
343
            return true;
344
345
        } // Inline frames (2):
0 ignored issues
show
Unused Code Comprehensibility introduced by
45% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
346
        else {
347
            if (in_array($display, Style::$INLINE_TYPES)) {
0 ignored issues
show
Documentation introduced by
The property $INLINE_TYPES is declared private in Dompdf\Css\Style. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
348
349
                // Avoid breaks within table-cells
350
                if ($this->_in_table) {
351
                    Helpers::dompdf_debug("page-break", "In table: " . $this->_in_table);
352
353
                    return false;
354
                }
355
356
                // Rule C
357
                $block_parent = $frame->find_block_parent();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Dompdf\Frame as the method find_block_parent() does only exist in the following sub-classes of Dompdf\Frame: Dompdf\FrameDecorator\AbstractFrameDecorator, Dompdf\FrameDecorator\Block, Dompdf\FrameDecorator\Image, Dompdf\FrameDecorator\Inline, Dompdf\FrameDecorator\ListBullet, Dompdf\FrameDecorator\ListBulletImage, Dompdf\FrameDecorator\NullFrameDecorator, Dompdf\FrameDecorator\Page, Dompdf\FrameDecorator\Table, Dompdf\FrameDecorator\TableCell, Dompdf\FrameDecorator\TableRow, Dompdf\FrameDecorator\TableRowGroup, Dompdf\FrameDecorator\Text. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
358
                if (count($block_parent->get_line_boxes()) < $frame->get_style()->orphans) {
0 ignored issues
show
Documentation introduced by
The property orphans does not exist on object<Dompdf\Css\Style>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
359
                    Helpers::dompdf_debug("page-break", "orphans");
360
361
                    return false;
362
                }
363
364
                // FIXME: Checking widows is tricky without having laid out the
365
                // remaining line boxes.  Just ignore it for now...
366
367
                // Rule D
368
                $p = $block_parent;
369 View Code Duplication
                while ($p) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
370
                    if ($p->get_style()->page_break_inside === "avoid") {
371
                        Helpers::dompdf_debug("page-break", "parent->inside: avoid");
372
373
                        return false;
374
                    }
375
                    $p = $p->find_block_parent();
376
                }
377
378
                // To prevent cascading page breaks when a top-level element has
379
                // page-break-inside: avoid, ensure that at least one frame with
380
                // some content is on the page before splitting.
381
                $prev = $frame->get_prev_sibling();
382
                while ($prev && ($prev->is_text_node() && trim($prev->get_node()->nodeValue) == "")) {
383
                    $prev = $prev->get_prev_sibling();
384
                }
385
386 View Code Duplication
                if ($block_parent->get_node()->nodeName === "body" && !$prev) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
387
                    // We are the body's first child
388
                    Helpers::dompdf_debug("page-break", "Body's first child.");
389
390
                    return false;
391
                }
392
393
                // Skip breaks on empty text nodes
394
                if ($frame->is_text_node() && $frame->get_node()->nodeValue == "") {
395
                    return false;
396
                }
397
398
                Helpers::dompdf_debug("page-break", "inline: break allowed");
399
400
                return true;
401
402
            // Table-rows
403
            } else {
404
                if ($display === "table-row") {
405
                    // Simply check if the parent table's page_break_inside property is
406
                    // not 'avoid'
407
                    $table = Table::find_parent_table($frame);
408
409
                    $p = $table;
410 View Code Duplication
                    while ($p) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
411
                        if ($p->get_style()->page_break_inside === "avoid") {
412
                            Helpers::dompdf_debug("page-break", "parent->inside: avoid");
413
414
                            return false;
415
                        }
416
                        $p = $p->find_block_parent();
417
                    }
418
419
                    // Avoid breaking after the first row of a table
420
                    if ($table && $table->get_first_child() === $frame || $table->get_first_child()->get_first_child() === $frame) {
421
                        Helpers::dompdf_debug("page-break", "table: first-row");
422
423
                        return false;
424
                    }
425
426
                    // If this is a nested table, prevent the page from breaking
427
                    if ($this->_in_table > 1) {
428
                        Helpers::dompdf_debug("page-break", "table: nested table");
429
430
                        return false;
431
                    }
432
433
                    Helpers::dompdf_debug("page-break", "table-row/row-groups: break allowed");
434
435
                    return true;
436
                } else {
437
                    if (in_array($display, Table::$ROW_GROUPS)) {
438
439
                        // Disallow breaks at row-groups: only split at row boundaries
440
                        return false;
441
442
                    } else {
443
                        Helpers::dompdf_debug("page-break", "? " . $frame->get_style()->display . "");
0 ignored issues
show
Documentation introduced by
The property display does not exist on object<Dompdf\Css\Style>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
444
445
                        return false;
446
                    }
447
                }
448
            }
449
        }
450
451
    }
452
453
    /**
454
     * Check if $frame will fit on the page.  If the frame does not fit,
455
     * the frame tree is modified so that a page break occurs in the
456
     * correct location.
457
     *
458
     * @param Frame $frame the frame to check
459
     *
460
     * @return bool
461
     */
462
    function check_page_break(Frame $frame)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
463
    {
464
        //FIXME: should not need to do this since we're tracking table status in `$this->_in_table`
465
        $p = $frame;
466
        $in_table = false;
467
        while ($p) {
468
            if ($p->is_table()) { $in_table = true; break; }
469
            $p = $p->get_parent();
470
        }
471
        // Do not split if we have already or if the frame was already
472
        // pushed to the next page (prevents infinite loops)
473
        if ($in_table) {
474
            if ($this->_page_full && $frame->_already_pushed) {
475
                return false;
476
            }
477
        } elseif ($this->_page_full || $frame->_already_pushed) {
478
            return false;
479
        }
480
481
        //FIXME: work-around for infinite loop due to tables 
482
        if ($in_table && $frame->_already_pushed) {
483
            return false;
484
        }
485
        $p = $frame;
486
        do {
487
            $display = $p->get_style()->display;
0 ignored issues
show
Documentation introduced by
The property display does not exist on object<Dompdf\Css\Style>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
488
            if ($display == "table-row") {
489
                if ($p->_already_pushed) { return false; }
490
            }
491
        } while ($p = $p->get_parent());
492
493
        // If the frame is absolute of fixed it shouldn't break
494
        $p = $frame;
495
        do {
496
            if ($p->is_absolute()) {
497
                return false;
498
            }
499
500
            // FIXME If the row is taller than the page and
501
            // if it the first of the page, we don't break
502
            $display = $p->get_style()->display;
0 ignored issues
show
Documentation introduced by
The property display does not exist on object<Dompdf\Css\Style>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
503
            if ($display === "table-row"
504
                && !$p->get_prev_sibling()
505
                && $p->get_margin_height() > $this->get_margin_height()
506
            ) {
507
                return false;
508
            }
509
        } while ($p = $p->get_parent());
510
511
        $margin_height = $frame->get_margin_height();
512
513
        // Determine the frame's maximum y value
514
        $max_y = (float)$frame->get_position("y") + $margin_height;
515
516
        // If a split is to occur here, then the bottom margins & paddings of all
517
        // parents of $frame must fit on the page as well:
518
        $p = $frame->get_parent();
519
        while ($p) {
520
            $max_y += $p->get_style()->computed_bottom_spacing();
521
            $p = $p->get_parent();
522
        }
523
524
525
        // Check if $frame flows off the page
526
        if ($max_y <= $this->_bottom_page_margin) {
527
            // no: do nothing
528
            return false;
529
        }
530
531
        Helpers::dompdf_debug("page-break", "check_page_break");
532
        Helpers::dompdf_debug("page-break", "in_table: " . $this->_in_table);
533
534
        // yes: determine page break location
535
        $iter = $frame;
536
        $flg = false;
537
538
        $in_table = $this->_in_table;
539
540
        Helpers::dompdf_debug("page-break", "Starting search");
541
        while ($iter) {
542
            // echo "\nbacktrack: " .$iter->get_node()->nodeName ." ".spl_object_hash($iter->get_node()). "";
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
543
            if ($iter === $this) {
544
                Helpers::dompdf_debug("page-break", "reached root.");
545
                // We've reached the root in our search.  Just split at $frame.
546
                break;
547
            }
548
549
            if ($this->_page_break_allowed($iter)) {
550
                Helpers::dompdf_debug("page-break", "break allowed, splitting.");
551
                $iter->split(null, true);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Dompdf\Frame as the method split() does only exist in the following sub-classes of Dompdf\Frame: Dompdf\FrameDecorator\AbstractFrameDecorator, Dompdf\FrameDecorator\Block, Dompdf\FrameDecorator\Image, Dompdf\FrameDecorator\Inline, Dompdf\FrameDecorator\ListBullet, Dompdf\FrameDecorator\ListBulletImage, Dompdf\FrameDecorator\NullFrameDecorator, Dompdf\FrameDecorator\Page, Dompdf\FrameDecorator\Table, Dompdf\FrameDecorator\TableCell, Dompdf\FrameDecorator\TableRow, Dompdf\FrameDecorator\TableRowGroup, Dompdf\FrameDecorator\Text. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
552
                $this->_page_full = true;
553
                $this->_in_table = $in_table;
554
                $frame->_already_pushed = true;
555
556
                return true;
557
            }
558
559 View Code Duplication
            if (!$flg && $next = $iter->get_last_child()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
560
                Helpers::dompdf_debug("page-break", "following last child.");
561
562
                if ($next->is_table()) {
563
                    $this->_in_table++;
564
                }
565
566
                $iter = $next;
567
                continue;
568
            }
569
570
            if ($next = $iter->get_prev_sibling()) {
571
                Helpers::dompdf_debug("page-break", "following prev sibling.");
572
573
                if ($next->is_table() && !$iter->is_table()) {
574
                    $this->_in_table++;
575
                } else if (!$next->is_table() && $iter->is_table()) {
576
                    $this->_in_table--;
577
                }
578
579
                $iter = $next;
580
                $flg = false;
581
                continue;
582
            }
583
584 View Code Duplication
            if ($next = $iter->get_parent()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
585
                Helpers::dompdf_debug("page-break", "following parent.");
586
587
                if ($iter->is_table()) {
588
                    $this->_in_table--;
589
                }
590
591
                $iter = $next;
592
                $flg = true;
593
                continue;
594
            }
595
596
            break;
597
        }
598
599
        $this->_in_table = $in_table;
600
601
        // No valid page break found.  Just break at $frame.
602
        Helpers::dompdf_debug("page-break", "no valid break found, just splitting.");
603
604
        // If we are in a table, backtrack to the nearest top-level table row
605
        if ($this->_in_table) {
606
            $iter = $frame;
607
            while ($iter && $iter->get_style()->display !== "table-row" && $iter->get_style()->display !== 'table-row-group' && $iter->_already_pushed === false) {
0 ignored issues
show
Documentation introduced by
The property display does not exist on object<Dompdf\Css\Style>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
608
                $iter = $iter->get_parent();
609
            }
610
611
            if ($iter) {
612
                $iter->split(null, true);
613
            } else {
614
                return false;
615
            }
616
        } else {
617
            $frame->split(null, true);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Dompdf\Frame as the method split() does only exist in the following sub-classes of Dompdf\Frame: Dompdf\FrameDecorator\AbstractFrameDecorator, Dompdf\FrameDecorator\Block, Dompdf\FrameDecorator\Image, Dompdf\FrameDecorator\Inline, Dompdf\FrameDecorator\ListBullet, Dompdf\FrameDecorator\ListBulletImage, Dompdf\FrameDecorator\NullFrameDecorator, Dompdf\FrameDecorator\Page, Dompdf\FrameDecorator\Table, Dompdf\FrameDecorator\TableCell, Dompdf\FrameDecorator\TableRow, Dompdf\FrameDecorator\TableRowGroup, Dompdf\FrameDecorator\Text. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
618
        }
619
620
        $this->_page_full = true;
621
        $frame->_already_pushed = true;
622
623
        return true;
624
    }
625
626
    //........................................................................
0 ignored issues
show
Unused Code Comprehensibility introduced by
100% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
627
628
    /**
629
     * @param Frame|null $frame
630
     * @param bool $force_pagebreak
631
     */
632
    function split(Frame $frame = null, $force_pagebreak = false)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
633
    {
634
        // Do nothing
635
    }
636
637
    /**
638
     * Add a floating frame
639
     *
640
     * @param Frame $frame
641
     *
642
     * @return void
643
     */
644
    function add_floating_frame(Frame $frame)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
645
    {
646
        array_unshift($this->_floating_frames, $frame);
647
    }
648
649
    /**
650
     * @return Frame[]
651
     */
652
    function get_floating_frames()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
653
    {
654
        return $this->_floating_frames;
655
    }
656
657
    /**
658
     * @param $key
659
     */
660
    public function remove_floating_frame($key)
661
    {
662
        unset($this->_floating_frames[$key]);
663
    }
664
665
    /**
666
     * @param Frame $child
667
     * @return int|mixed
668
     */
669
    public function get_lowest_float_offset(Frame $child)
670
    {
671
        $style = $child->get_style();
672
        $side = $style->clear;
0 ignored issues
show
Documentation introduced by
The property clear does not exist on object<Dompdf\Css\Style>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
673
        $float = $style->float;
0 ignored issues
show
Documentation introduced by
The property float does not exist on object<Dompdf\Css\Style>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
674
675
        $y = 0;
676
677
        if ($float === "none") {
678
            foreach ($this->_floating_frames as $key => $frame) {
679
                if ($side === "both" || $frame->get_style()->float === $side) {
680
                    $y = max($y, $frame->get_position("y") + $frame->get_margin_height());
681
                }
682
                $this->remove_floating_frame($key);
683
            }
684
        }
685
686
        if ($y > 0) {
687
            $y++; // add 1px buffer from float
688
        }
689
690
        return $y;
691
    }
692
}
693