Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
90.00% covered (success)
90.00%
18 / 20
33.33% covered (danger)
33.33%
1 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
Template_Placements
90.00% covered (success)
90.00%
18 / 20
33.33% covered (danger)
33.33%
1 / 3
10.10
0.00% covered (danger)
0.00%
0 / 1
 get
83.33% covered (warning)
83.33%
5 / 6
0.00% covered (danger)
0.00%
0 / 1
5.12
 update
80.00% covered (warning)
80.00%
4 / 5
0.00% covered (danger)
0.00%
0 / 1
2.03
 sanitize
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2/**
3 * Reads and saves where the Sharing Buttons and Like blocks are added to templates.
4 *
5 * @package automattic/jetpack-sharing-likes
6 */
7
8declare( strict_types = 1 );
9
10namespace Automattic\Jetpack\Sharing_Likes\Hooked_Blocks;
11
12/**
13 * Where each block is hooked into the single post and page templates: before the
14 * post content or after it, never both. One autoloaded option per block, holding a list.
15 *
16 * @since $$next-version$$
17 */
18final class Template_Placements {
19
20    /**
21     * The Sharing Buttons block.
22     */
23    public const FEATURE_SHARING = 'sharing';
24
25    /**
26     * The Like block.
27     */
28    public const FEATURE_LIKES = 'likes';
29
30    /**
31     * Before the post content, in single post and page templates.
32     */
33    public const BEFORE_CONTENT = 'before_content';
34
35    /**
36     * After the post content, in single post and page templates.
37     */
38    public const AFTER_CONTENT = 'after_content';
39
40    /**
41     * Every placement, in the order they are stored.
42     */
43    public const PLACEMENTS = array( self::BEFORE_CONTENT, self::AFTER_CONTENT );
44
45    /**
46     * Option holding each feature's placements.
47     */
48    public const OPTIONS = array(
49        self::FEATURE_SHARING => 'jetpack_sharing_buttons_auto_add',
50        self::FEATURE_LIKES   => 'jetpack_likes_auto_add',
51    );
52
53    /**
54     * The placements a feature is set to, known values only.
55     *
56     * The Sharing option once held a boolean that meant "after the content", so a truthy scalar still does.
57     *
58     * @param string $feature One of the FEATURE_* constants.
59     * @return string[]
60     */
61    public static function get( string $feature ): array {
62        if ( ! isset( self::OPTIONS[ $feature ] ) ) {
63            return array();
64        }
65
66        $stored = get_option( self::OPTIONS[ $feature ], array() );
67
68        if ( ! is_array( $stored ) ) {
69            return self::FEATURE_SHARING === $feature && $stored ? array( self::AFTER_CONTENT ) : array();
70        }
71
72        return self::sanitize( $stored );
73    }
74
75    /**
76     * Save a feature's placements. The one writer for both options.
77     *
78     * @param string $feature    One of the FEATURE_* constants.
79     * @param array  $placements Placements to save; unknown values, duplicates and a second side of the content are dropped.
80     * @return string[] The placements as saved.
81     */
82    public static function update( string $feature, array $placements ): array {
83        if ( ! isset( self::OPTIONS[ $feature ] ) ) {
84            return array();
85        }
86
87        $placements = self::sanitize( $placements );
88        update_option( self::OPTIONS[ $feature ], $placements, true );
89
90        return $placements;
91    }
92
93    /**
94     * Known placements only, each once, in `PLACEMENTS` order, with at most one side of the content.
95     *
96     * Core inserts a block hooked on both sides of one anchor only before it, so after wins, as the legacy option meant.
97     *
98     * @param array $placements Placements, possibly from untrusted input.
99     * @return string[]
100     */
101    private static function sanitize( array $placements ): array {
102        $after = in_array( self::AFTER_CONTENT, $placements, true );
103
104        return array_values(
105            array_filter(
106                self::PLACEMENTS,
107                static function ( string $placement ) use ( $placements, $after ): bool {
108                    return in_array( $placement, $placements, true ) && ! ( $after && self::BEFORE_CONTENT === $placement );
109                }
110            )
111        );
112    }
113}