Completed
Push — master ( 9c0738...41953e )
by Askupa
01:56
created

Manager.php (1 issue)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace Amarkal\Metabox;
4
5
/**
6
 * Metabox Manager adds metaboxes to WordPress posts
7
 */
8
class Manager
9
{
10
    /**
11
     * @var Singleton The reference to *Singleton* instance of this class
12
     */
13
    private static $instance;
14
    
15
    /**
16
     * @var Array Stores all the registered metaboxes
17
     */
18
    private $metaboxes = array();
19
    
20
    /**
21
     * Security nonce action
22
     */
23
    const NONCE_ACTION = 'amarkal_metabox';
24
    
25
    /**
26
     * Returns the *Singleton* instance of this class.
27
     *
28
     * @return Singleton The *Singleton* instance.
29
     */
30
    public static function get_instance()
31
    {
32
        if( null === static::$instance ) 
33
        {
34
            static::$instance = new static();
35
            static::$instance->init();
0 ignored issues
show
Since $instance is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $instance to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
36
        }
37
        return static::$instance;
38
    }
39
    
40
    /**
41
     * Add a metabox.
42
     * 
43
     * @param string $id
44
     * @param array $args
45
     * @throws \RuntimeException if the given metabox id has already been registered
46
     */
47
    public function add( $id, array $args )
48
    {
49
        if( !in_array($id, $this->metaboxes) )
50
        {
51
            $this->metaboxes[$id] = array_merge($this->default_args(), $args);
52
        }
53
        else throw new \RuntimeException("A metabox with id '$id' has already been registered.");
54
    }
55
    
56
    /**
57
     * Render a metabox.
58
     * 
59
     * @param WP_Post $post
60
     * @param array $args
61
     */
62
    public function render( $post, $args )
63
    {
64
        $metabox = $this->metaboxes[$args['id']];
65
        wp_nonce_field(self::NONCE_ACTION, $args['id'].'_nonce');
66
        foreach( $metabox['fields'] as $field )
67
        {
68
            $field['post_id'] = $post->ID;
69
            $field_template = new Field($field);
70
            echo $field_template->render();
71
        }
72
    }
73
    
74
    /**
75
     * Internally used to register metaboxes.
76
     */
77
    public function add_meta_boxes()
78
    {
79
        foreach( $this->metaboxes as $id => $args )
80
        {
81
            \add_meta_box(
82
                $id,
83
                $args['title'],
84
                array($this, 'render'),
85
                $args['screen'],
86
                $args['context'],
87
                $args['priority']
88
            );
89
        }
90
    }
91
    
92
    /**
93
     * Save metaboxes data for a given page.
94
     * 
95
     * @param number $post_id
96
     */
97
    public function save_meta_boxes( $post_id )
98
    {
99
        /**
100
         * A note on security:
101
         * 
102
         * We need to verify this came from the our screen and with proper authorization,
103
         * because save_post can be triggered at other times. since metaboxes can 
104
         * be removed - by having a nonce field in only one metabox there is no 
105
         * guarantee the nonce will be there. By placing a nonce field in each 
106
         * metabox you can check if data from that metabox has been sent 
107
         * (and is actually from where you think it is) prior to processing any data.
108
         * @see http://wordpress.stackexchange.com/a/49460/25959
109
         */
110
 
111
        /*
112
         * If this is an autosave, our form has not been submitted,
113
         * so we don't want to do anything.
114
         */
115
        if( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) 
116
        {
117
            return $post_id;
118
        }
119
120
        // Check the user's permissions.
121
        $post_type = filter_input(INPUT_POST, 'post_type');
122
        if( null !== $post_type && !current_user_can('edit_'.$post_type, $post_id) )
123
        {
124
            return $post_id;
125
        }
126
127
        // Update the meta fields.
128
        foreach( $this->metaboxes as $id => $metabox )
129
        {
130
            $this->save_meta_box( $post_id, $id, $metabox );
131
        }
132
    }
133
    
134
    /**
135
     * Save the data of a single metabox.
136
     * 
137
     * @param number $post_id
138
     * @param string $id
139
     * @param array $metabox
140
     */
141
    public function save_meta_box( $post_id, $id, $metabox )
142
    {
143
        $nonce_name  = $id.'_nonce';
144
        $nonce_value = filter_input(INPUT_POST, $nonce_name);
145
        
146
        // Check if our nonce is set.
147
        if( null === $nonce_value ) 
148
        {
149
            return $post_id;
150
        }
151
152
        // Verify that the nonce is valid.
153
        if ( !wp_verify_nonce($nonce_value, self::NONCE_ACTION) ) 
154
        {
155
            return $post_id;
156
        }
157
        
158
        foreach( $metabox['fields'] as $field )
159
        {
160
            $data = filter_input( INPUT_POST, $field['name'] );
161
            \update_post_meta( $post_id, $field['name'], $data );
162
        }
163
    }
164
    
165
    /**
166
     * Print custom metabox style.
167
     */
168
    public function print_style() 
169
    {
170
        $cs = get_current_screen();
171
        
172
        foreach( $this->metaboxes as $metabox )
173
        {
174
            if( $metabox['screen'] === $cs->id )
175
            {
176
                echo '<style>';
177
                include 'metabox.css';
178
                echo '</style>';
179
                return;
180
            }
181
        }
182
    }
183
    
184
    /**
185
     * Initiate the metaboxes by adding action hooks for printing and saving.
186
     */
187
    private function init()
188
    {
189
        \add_action( 'add_meta_boxes', array( $this, 'add_meta_boxes' ) );
190
        \add_action( 'save_post', array( $this, 'save_meta_boxes' ) );
191
        \add_action( 'admin_footer', array( $this, 'print_style' ) );
192
    }
193
    
194
    /**
195
     * Default arguments for the add() method.
196
     * 
197
     * @return array
198
     */
199
    private function default_args()
200
    {
201
        return array(
202
            'title'    => null,
203
            'screen'   => null,
204
            'context'  => 'advanced',
205
            'priority' => 'default',
206
            'fields'   => array()
207
        );
208
    }
209
}