├── .circleci
└── config.yml
├── .gitignore
├── README.md
├── composer.json
├── functions.php
├── phpunit.xml.dist
├── src
├── Attachment.php
├── CacheHandler
│ ├── BaseHandler.php
│ ├── PostCache.php
│ ├── TermCache.php
│ └── UserCache.php
├── Meta.php
├── Page.php
├── Picture.php
├── Plugin
│ ├── Plugin.php
│ ├── PluginFactory.php
│ └── Service.php
├── Post.php
├── Term.php
└── User.php
├── tests
├── bin
│ └── install-wp-tests.sh
├── bootstrap.php
├── test-post-controller.php
└── test-term-controller.php
├── vendor
├── autoload.php
└── composer
│ ├── ClassLoader.php
│ ├── LICENSE
│ ├── autoload_classmap.php
│ ├── autoload_namespaces.php
│ ├── autoload_psr4.php
│ ├── autoload_real.php
│ └── autoload_static.php
└── wp-controllers.php
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 |
3 | job-references:
4 | mysql_image: &mysql_image
5 | circleci/mysql:5.6
6 |
7 | php_job: &php_job
8 | environment:
9 | - WP_TESTS_DIR: "/tmp/wordpress-tests-lib"
10 | - WP_CORE_DIR: "/tmp/wordpress/"
11 | - CIRCLE_PROJECT_REPONAME: "WP-Controllers"
12 | steps:
13 | - checkout
14 | - run:
15 | name: "Setup Environment Variables"
16 | command: |
17 | echo "export PATH=$HOME/.composer/vendor/bin:$PATH" >> $BASH_ENV
18 | source /home/circleci/.bashrc
19 | - run:
20 | name: "Install Dependencies"
21 | command: |
22 | sudo apt-get update && sudo apt-get install git subversion
23 | sudo docker-php-ext-install mysqli
24 | sudo sh -c "printf '\ndeb http://ftp.us.debian.org/debian sid main\n' >> /etc/apt/sources.list"
25 | sudo apt-get update && sudo apt-get install mysql-client-5.7
26 |
27 | - run:
28 | name: "Setup For Tests"
29 | command: |
30 | composer global require "phpunit/phpunit=5.7.*"
31 | rm -rf $WP_TESTS_DIR $WP_CORE_DIR
32 | bash tests/bin/install-wp-tests.sh wordpress_test root '' 127.0.0.1
33 |
34 | - run:
35 | name: "Run Tests"
36 | command: phpunit
37 |
38 |
39 | jobs:
40 | php56-build:
41 | <<: *php_job
42 | docker:
43 | - image: circleci/php:5.6
44 | - image: *mysql_image
45 |
46 | php70-build:
47 | <<: *php_job
48 | docker:
49 | - image: circleci/php:7.0
50 | - image: *mysql_image
51 |
52 | php71-build:
53 | <<: *php_job
54 | docker:
55 | - image: circleci/php:7.1
56 | - image: *mysql_image
57 |
58 | php72-build:
59 | <<: *php_job
60 | docker:
61 | - image: circleci/php:7.2
62 | - image: *mysql_image
63 |
64 | workflows:
65 | version: 2
66 | main:
67 | jobs:
68 | - php56-build
69 | - php70-build
70 | - php71-build
71 | - php72-build
72 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://circleci.com/gh/papertower/WP-Controllers)
2 |
3 | ## WordPress development for the [OOP](https://en.wikipedia.org/wiki/Object-oriented_programming) Programmer
4 | #### So what's the story?
5 | WordPress is an excellent framework. It's secure, robust, extensible, and is fanatical about legacy support. What it's not, however, is strongly written using modern techniques. Please understand, that's not a sleight at the expense of the hard-working contributors. A lot of the code in WordPress was written years ago for versions of PHP that preceded the mass amounts of improvements made in PHP 5.3.x. The other side of it is that it's catered towards non-developers. That is, folks who want to make a website, and are willing to learn a little PHP, but have no intention of learning more than they need to for their site. And that's ok! But, for those who do want to develop in a stronger, more OOP manner, it can be a drag.
6 |
7 | #### Enter WP Controllers!
8 | The WP Controllers were developed by a developer for developers. It's an attempt to take the base objects in WordPress (posts, terms, users, etc.) and give a class for each one. Instead of memorizing a lot of obscure functions, you simply handle the object and use all its provided functions. Not only does it simplify things, but it also speeds up development. A lot.
9 |
10 | #### Magic sucks
11 | OK, magic is fun, but not when developing. At times something working, but not knowing why, can be just as frustrating as a bug. Gah! WordPress, unfortunately, in the name of being friendly for non-developers, does a lot of magic. For example, when inside "the loop" of a wp_query, you're meant to use all these crazy functions that iterate the loop, grab the post in the loop, display specific things based on the current iteration of the loop, etc.. Wow! What ever happened to a foreach loop!?
12 |
13 | Ultimately, WordPress is a database-driven PHP framework. Nothing more, nothing less. The database is MySQL, and it operates using SQL. You can interact with the information like any other database, and the framework itself can be extended and amended like any other framework. No magic. The goal should be to be able to get all the benefits of the WordPress framework, without sacrificing the natural (and growing) capabilities of PHP and SQL.
14 |
15 | #### In a nutshell
16 | These classes make it possible to feel like you're working in PHP while enjoying all the benefits of WordPress. You lose nothing by using them, and it doesn't break *anything* in Wordpress. You're free to use as much or as little of these as you'd like. If you are (or aspire to be) a PHP developer, then you'll probably really like using this; if not, it may overwhelm you, and that's no problem.
17 |
18 | #### How to use it
19 | WP Controllers is a plugin, so just drop it in your plugins directory and activate it. From there you'll get access to all the base controllers.
20 |
21 | To extend the controllers just create a `wp-controllers/` directory in your root theme or plugin, and place your controllers in that directory. It is important that your naming convention for the controllers is that the name matches the file. So if your class name is `Services` then your file should be `services.php`.
22 |
23 | ##### Get a controller
24 | Easy as that. Now, to get a controller (e.g. post), use the `get_post_controller` function. Simply pass a post id, slug, or object. Don't worry about type casting, it will figure itself out. Or, if you're inside a post single template (or page), just call
25 | ```
26 | $Post = get_post_controller()
27 | ```
28 | with no arguments and it will return the controller for the active post.
29 |
30 | ##### Use the controller
31 | Once you have the controller, all the standard properties will be available minus the 'post_' prefix. So post_type, for example, would be
32 | ```
33 | $Post->type
34 | ```
35 | Meta is accessed via the Meta class, which is automatically added to every controller. You can retrieve single or all values and apply functions to the results
36 |
37 | ```
38 | // Retrieves single meta
39 | $Post->meta->sub_title
40 |
41 | // All meta or empty array
42 | $Post->meta->all_movies('all')
43 |
44 | // The controllers for every value
45 | $Post->meta->related_posts('controllers')
46 | ```
47 |
48 | ##### Familiarize yourself
49 | The best thing to do from here would be to look through the controllers and check out the available functions. There's some pretty cool ones and they keep being added. For example,
50 |
51 | ```
52 | $Post->terms($taxonomies)
53 | ```
54 |
55 | returns all the term controllers for that post. Another one is
56 |
57 | ```
58 | $Post->excerpt(50)
59 | ```
60 |
61 | which would return either all the excerpt (if used and has actual content) or the first 50 (or however many you want) **words** of the content, with all the shortcodes and tags stripped.
62 |
63 | #### Get Involved
64 | Have an idea? Make a pull request!
65 |
66 | Have a problem or question? Make an issue!
67 |
68 | This isn't just to make WordPress more object-oriented, it's also to make development faster, less buggy, and more efficient. It's a fantastic way to fulfill the [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) principal.
69 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "papertower/wp-controllers",
3 | "description": "Controllers to work in WordPress the OOP way",
4 | "type": "wordpress-plugin",
5 | "license": "MIT",
6 | "authors": [
7 | {
8 | "name": "Jason Adams",
9 | "email": "jason@papertower.com"
10 | }
11 | ],
12 | "require": {},
13 | "autoload": {
14 | "psr-4": {"WPControllers\\": "src/"}
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/functions.php:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 | ./tests/
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/Attachment.php:
--------------------------------------------------------------------------------
1 | description =& $this->content;
35 | $this->caption =& $this->excerpt;
36 | $this->mime_type =& $post->post_mime_type;
37 | }
38 |
39 | public function alt() {
40 | return $this->meta->_wp_attachment_image_alt;
41 | }
42 |
43 | public function mime_type() {
44 | return $this->mime_type;
45 | }
46 |
47 | public function link() {
48 | return isset($this->_link) ? $this->_link
49 | : $this->_link = wp_get_attachment_url($this->id);
50 | }
51 |
52 | public function path() {
53 | return isset($this->_path) ? $this->_path
54 | : $this->_path = get_attached_file($this->id);
55 | }
56 |
57 | public function file_size() {
58 | return isset($this->_file_size) ? $this->_file_size
59 | : filesize($this->path());
60 | }
61 |
62 | /**
63 | * @return string
64 | */
65 | public function file_type() {
66 | $type = explode('/', $this->mime_type);
67 | return empty($type[1]) ? '' : strtoupper($type[1]);
68 | }
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/src/CacheHandler/BaseHandler.php:
--------------------------------------------------------------------------------
1 | post_type ) {
27 | $this->trigger_flush($post, $is_update ? self::EVENT_UPDATE : self::EVENT_INSERT);
28 | }
29 | }
30 |
31 | /**
32 | * Callback for the before_delete_post action, used to invalidate the cache
33 | * @ignore
34 | */
35 | public function before_delete_post($post_id) {
36 | $post = get_post($post_id);
37 | if ( 'revision' !== $post && !did_action('before_delete_post') ) {
38 | $this->trigger_flush($post, self::EVENT_DELETE);
39 | }
40 | }
41 |
42 | /**
43 | * Triggers the flush to cache with the Post controller
44 | * @param WP_Post $post Post being affected
45 | * @param string $event Event triggering the flush
46 | */
47 | private function trigger_flush($post, $event) {
48 | Post::trigger_cache_flush($post, $event);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/CacheHandler/TermCache.php:
--------------------------------------------------------------------------------
1 | trigger_flush(get_term($term_id, $taxonomy), self::EVENT_INSERT);
37 | }
38 |
39 | /**
40 | * Triggers when an existing term is updated
41 | * @param integer $term_id New term id
42 | * @param integer $term_taxonomy_id New term taxonomy id
43 | * @param string $taxonomy Taxonomy of new term
44 | */
45 | public function edit_term($term_id, $term_taxonomy_id, $taxonomy) {
46 | $this->trigger_flush(get_term($term_id, $taxonomy), self::EVENT_UPDATE);
47 | }
48 |
49 | /**
50 | * Triggers when an existing term is deleted
51 | * @param integer $term_id New term id
52 | * @param string $taxonomy Taxonomy of new term
53 | */
54 | public function pre_delete_term($term_id, $taxonomy) {
55 | $this->trigger_flush(get_term($term_id, $taxonomy), self::EVENT_DELETE);
56 | }
57 |
58 | /**
59 | * Triggers the flush to cache with the Term controller
60 | * @param WP_Term $term Term being affected
61 | * @param string $event Event triggering the flush
62 | */
63 | private function trigger_flush($term, $event) {
64 | Term::trigger_flush_cache($term, $event);
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/CacheHandler/UserCache.php:
--------------------------------------------------------------------------------
1 | trigger_flush(get_user_by('id', $user_id), self::EVENT_INSERT);
35 | }
36 |
37 | /**
38 | * Triggers when a user is updated
39 | * @param int $user_id
40 | * @param WP_User $old_user
41 | */
42 | public function profile_update($user_id, $old_user) {
43 | $this->trigger_flush($old_user, self::EVENT_UPDATE);
44 | }
45 |
46 | /**
47 | * Triggers when a user is deleted
48 | * @param int $user_id
49 | * @param int $reassign_id
50 | */
51 | public function delete_user($user_id, $reassign_id) {
52 | $this->trigger_flush(get_user_by('id', $user_id), self::EVENT_DELETE);
53 | }
54 |
55 | /**
56 | * Triggers the flush to cache with the User controller
57 | * @param WP_User $user User being affected
58 | * @param string $event Event triggering the flush
59 | */
60 | private function trigger_flush($user, $event) {
61 | User::trigger_flush_cache($user, $event);
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/Meta.php:
--------------------------------------------------------------------------------
1 | object_id = $id;
29 | $this->object_type = $type;
30 | }
31 |
32 | /**
33 | * @param string $name
34 | * @param array $arguments
35 | *
36 | * @return mixed
37 | */
38 | public function __call($name, $arguments) {
39 | $this->get_meta($name);
40 |
41 | if ( empty($arguments) ) {
42 | return $this->data[$name];
43 | }
44 |
45 | $function = array_shift($arguments);
46 | array_unshift($arguments, $this->data[$name]);
47 |
48 | if ( is_string($function) && method_exists($this, $function) ) {
49 | $function = array($this, $function);
50 | }
51 |
52 | if ( is_callable($function) ) {
53 | return call_user_func_array($function, $arguments);
54 | } else {
55 | return $this->data[$name];
56 | }
57 | }
58 |
59 | /**
60 | * @param string $name
61 | *
62 | * @return mixed
63 | */
64 | public function __get($name) {
65 | $this->get_meta($name);
66 |
67 | if ( is_array($this->data[$name]) ) {
68 | switch(count($this->data[$name])) {
69 | case 0: return '';
70 | case 1: return $this->data[$name][0];
71 | default: return $this->data[$name];
72 | }
73 | } else {
74 | return $this->data[$name];
75 | }
76 | }
77 |
78 | /**
79 | * @param string $name
80 | *
81 | * @return bool
82 | */
83 | public function __isset($name) {
84 | if ( !empty($this->data[$name]) ) {
85 | return true;
86 | }
87 |
88 | $this->get_meta($name);
89 | return !empty($this->data[$name]);
90 | }
91 |
92 | /**
93 | * @param string $name
94 | */
95 | public function __unset($name) {
96 | unset($this->data[$name]);
97 | }
98 |
99 | /**
100 | * Used to retrieve meta with a key not permitted by PHP
101 | * For example, if a key is 'my-field', the hyphen is not set
102 | * @param string $key key used in the database
103 | * @param string $name name to store under for future retrieval
104 | */
105 | public function store($key, $name) {
106 | $this->get_meta($key, $name);
107 | }
108 |
109 | /**
110 | * @param string $key
111 | * @param null $name
112 | * @param bool|false $single
113 | */
114 | protected function get_meta($key, $name = null, $single = false) {
115 | $name = is_null($name) ? $key : $name;
116 | if ( isset($this->data[$name]) ) return;
117 |
118 | switch ($this->object_type) {
119 | case 'post':
120 | case 'user':
121 | case 'comment':
122 | $this->data[$name] = get_metadata($this->object_type, $this->object_id, $key, $single);
123 | break;
124 |
125 | case 'term':
126 | $this->data[$name] = get_term_meta($this->object_id, $key, $single);
127 | break;
128 | }
129 | }
130 |
131 | /**
132 | * @param array $value
133 | *
134 | * @return mixed|null
135 | */
136 | protected function single($value) {
137 | return isset($value[0]) ? $value[0] : null;
138 | }
139 |
140 | /**
141 | * @param $value
142 | *
143 | * @return mixed|null
144 | */
145 | protected function all($value) {
146 | return is_array($value) ? $value : array();
147 | }
148 |
149 | protected function controllers($values) {
150 | if ( is_array($values) ) {
151 | if ( !empty($values) ) {
152 | return get_post_controllers($values);
153 | } else {
154 | return array();
155 | }
156 | } else {
157 | return $values;
158 | }
159 | }
160 |
161 | /**
162 | * @param array $values
163 | *
164 | * @return Post|null
165 | */
166 | protected function controller($values) {
167 | if ( is_array($values) && !empty($values[0]) ) {
168 | return get_post_controller($values[0]);
169 | } else {
170 | return null;
171 | }
172 | }
173 |
174 | protected function date($values, $format) {
175 | if ( empty($values[0]) ) return '';
176 | return date($format, strtotime($values[0]));
177 | }
178 |
179 | }
180 |
--------------------------------------------------------------------------------
/src/Page.php:
--------------------------------------------------------------------------------
1 | _children ) return $this->_children;
40 |
41 | $this->_children = self::get_controllers(array(
42 | 'hierarchical' => false,
43 | 'parent' => $this->id,
44 | 'post_status' => 'publish,private'
45 | ));
46 |
47 | return $this->_children;
48 | }
49 |
50 | /**
51 | * Returns page parent controller, if any
52 | * @return object|null
53 | */
54 | public function parent() {
55 | if ( $this->_parent ) return $this->_parent;
56 | if ( !$this->parent_id ) return null;
57 |
58 | return $this->_parent = self::get_controller($this->parent_id);
59 | }
60 |
61 | /**
62 | * Returns pages of specified template(s)
63 | * @param array|string $templates
64 | * @param array $options (optional)
65 | * @return array
66 | */
67 | public static function get_page_templates($templates, $options = array()) {
68 | $options = array_merge_recursive($options, array(
69 | 'post_type' => 'page',
70 | 'numberposts' => -1,
71 | 'meta_query' => array(
72 | array(
73 | 'key' => '_wp_page_template',
74 | 'value' => $templates
75 | )
76 | )
77 | ));
78 |
79 | return parent::get_controllers($options);
80 | }
81 |
82 | /**
83 | * Returns parent controller for post in loop
84 | * @return object
85 | */
86 | public static function get_parent() {
87 | global $post;
88 | return ( $post->post_parent ) ? self::get_controller($post->post_parent) : null;
89 | }
90 |
91 | }
92 |
--------------------------------------------------------------------------------
/src/Picture.php:
--------------------------------------------------------------------------------
1 | sizes[$size]) )
25 | $this->sizes[$size] = wp_get_attachment_image_src($this->id, $size);
26 |
27 | return $this->sizes[$size];
28 | }
29 |
30 | /**
31 | * src
32 | * Returns the image src attribute for a given size. If $wide_size is provided the returned src
33 | * will depend on the original dimensions of the image. If the image is square then either
34 | * $square_size will be used or $size by default.
35 | *
36 | * @param string $size
37 | * @param string $wide_size
38 | * @param string $square_size
39 | *
40 | * @return string
41 | */
42 | public function src($size, $wide_size = null, $square_size = null) {
43 | if ( !$wide_size ) {
44 | $details = $this->details($size);
45 | return $details[0];
46 | } else {
47 | $width = $this->width('full');
48 | $height = $this->height('full');
49 |
50 | if ( $width > $height ) {
51 | $details = $this->details($wide_size);
52 | } elseif ( $height > $width ) {
53 | $details = $this->details($size);
54 | } else {
55 | $details = $square_size ? $this->details($square_size) : $this->details($size);
56 | }
57 |
58 | return $details[0];
59 | }
60 | }
61 |
62 | /**
63 | * srcset.
64 | * Accepts an associative array where the key is the qualifier (e.g. 200w) and the value is the
65 | * size to display. If an array is passed as the value then it follows the same parameters as the
66 | * src function.
67 | *
68 | * A string can also be provided that's parsed into the usable parts:
69 | * 200w->small|600w->medium|1200->tall-large,tall-wide
70 | *
71 | * @param array|string $sizes associative array of sizes
72 | *
73 | * @return string
74 | */
75 | public function srcset($sizes) {
76 | if ( is_string($sizes) ) {
77 | $sizes = explode('|', $sizes);
78 | $parsed_sizes = array();
79 | foreach($sizes as $pair) {
80 | list($qualifier, $size) = explode('->', $pair);
81 | $parsed_sizes[trim($qualifier)] = explode(',', trim($size));
82 | }
83 | $sizes = $parsed_sizes;
84 | }
85 |
86 | $list = array();
87 | foreach($sizes as $qualifier => $size) {
88 | $src = is_array($size) ? call_user_func_array(array($this, 'src'), $size) : $this->src($size);
89 | $list[] = "$src $qualifier";
90 | }
91 |
92 | return implode(', ', $list);
93 | }
94 |
95 | /**
96 | * @param string $size
97 | *
98 | * @return int
99 | */
100 | public function width($size) {
101 | $details = $this->details($size);
102 | return $details[1];
103 | }
104 |
105 | /**
106 | * @param string $size
107 | *
108 | * @return int
109 | */
110 | public function height($size) {
111 | $details = $this->details($size);
112 | return $details[2];
113 | }
114 |
115 | /**
116 | * @param string $size
117 | *
118 | * @return bool
119 | */
120 | public function is_resized($size) {
121 | $details = $this->details($size);
122 | return $details[3];
123 | }
124 | };
125 |
--------------------------------------------------------------------------------
/src/Plugin/Plugin.php:
--------------------------------------------------------------------------------
1 |
17 | */
18 | private $_directories = [];
19 |
20 | /**
21 | * Main file of the WP Controllers plugin
22 | * @var string
23 | */
24 | private $plugin_file;
25 |
26 | /**
27 | * Main directory of the WP Controllers plugin
28 | * @var string
29 | */
30 | private $plugin_directory;
31 |
32 | /**
33 | * Constructor for plugin which sets the plugin file and directory
34 | * @param string $file Main plugin file
35 | * @param string $directory Main plugin directory
36 | */
37 | public function __construct($file, $directory) {
38 | $this->plugin_file = $file;
39 | $this->plugin_directory = $directory;
40 | }
41 |
42 | /**
43 | * Kicks everything off
44 | */
45 | public function register() {
46 | add_action('init', [$this, 'init']);
47 | add_filter('register_post_type_args', [$this, 'register_post_type_args'], 10, 2);
48 |
49 | $this->registerCacheServices();
50 |
51 | require_once "{$this->plugin_directory}/functions.php";
52 | }
53 |
54 | /**
55 | * Loads all the controller classes upon the init event of WordPress
56 | */
57 | public function init() {
58 | $this->load_controller_directories();
59 |
60 | spl_autoload_register([$this, 'autoload_register']);
61 | }
62 |
63 | public function registerCacheServices() {
64 | $services = [
65 | PostCache::class,
66 | TermCache::class,
67 | UserCache::class
68 | ];
69 |
70 | foreach($services as $service) {
71 | $service = new $service;
72 | $service->register();
73 | }
74 | }
75 |
76 | /**
77 | * Sets the controllers classes for the default post types
78 | * @param array $args Post type registration arguments
79 | * @param string $post_type Post type being registered
80 | * @return array Modified registration arguments
81 | */
82 | public function register_post_type_args($args, $post_type) {
83 | switch($post_type) {
84 | case 'post':
85 | $args['wp_controller_class'] = apply_filters("wp_controllers_default_{$post_type}_class", Post::class, $args);
86 | break;
87 |
88 | case 'attachment':
89 | $args['wp_controller_class'] = apply_filters("wp_controllers_default_{$post_type}_class", Attachment::class, $args);
90 | break;
91 |
92 | case 'page':
93 | $args['wp_controller_class'] = apply_filters("wp_controllers_default_{$post_type}_class", Page::class, $args);
94 | break;
95 | }
96 |
97 | return $args;
98 | }
99 |
100 | /**
101 | * autoload_register.
102 | * Used to autoload the classes in order of inheritance
103 | * @param string $class the class name
104 | */
105 | public function autoload_register($class) {
106 | // Checks for file in lowerclass
107 | $lower_class = function_exists('mb_strtolower') ? mb_strtolower($class) : strtolower($class);
108 |
109 | // Checks for class with namespacing stripped
110 | $namespace_position = strrpos($class, '\\');
111 | $base_class = $namespace_position ? substr($class, -1 * ( strlen($class) - $namespace_position - 1 ) ) : $class;
112 |
113 | foreach($this->_directories as $directory) {
114 | if ( file_exists("$directory/$base_class.php") ) {
115 | $include = "$directory/$base_class.php";
116 | } elseif ( file_exists("$directory/$lower_class.php") ) {
117 | $include = "$directory/$lower_class.php";
118 | } else {
119 | continue;
120 | }
121 |
122 | include $include;
123 | if ( class_exists($class, false) && method_exists($class, '_construct')) {
124 | call_user_func([$class, '_construct']);
125 | }
126 |
127 | break;
128 | }
129 | }
130 |
131 | /**
132 | * Goes through the theme and active plugins to check whether it has a wp-controllers directory
133 | * and adds this to the internal directories
134 | */
135 | private function load_controller_directories() {
136 | $this->_directories = ["{$this->plugin_directory}/controllers"];
137 |
138 | $parent_theme = get_template_directory();
139 | $child_theme = get_stylesheet_directory();
140 |
141 | // Check & add child theme
142 | if ( $parent_theme !== $child_theme ) {
143 | $child_theme = apply_filters('wp_controllers_child_theme_directory', "$child_theme/wp-controllers");
144 | if ( is_dir($child_theme) ) {
145 | $this->_directories[] = $child_theme;
146 | $this->trigger_deprecation_notice($child_theme);
147 | }
148 | }
149 |
150 | // Check & add main/parent theme
151 | $parent_theme = apply_filters('wp_controllers_theme_directory', "$parent_theme/wp-controllers");
152 | if ( is_dir($parent_theme) ) {
153 | $this->_directories[] = $parent_theme;
154 | $this->trigger_deprecation_notice($parent_theme);
155 | }
156 |
157 | // Include necessary plugin functions if front-end
158 | if ( !function_exists('get_plugins') ) {
159 | include_once( ABSPATH . 'wp-admin/includes/plugin.php' );
160 | }
161 |
162 | // Check & add active plugins
163 | $plugins = get_plugins();
164 | $plugins_path = WP_PLUGIN_DIR;
165 | foreach($plugins as $path => $data) {
166 | if ( is_plugin_active($path) && basename($path) !== basename($this->plugin_file) ) {
167 | $path = strstr($path, DIRECTORY_SEPARATOR, true);
168 | $directory = apply_filters('wp_controllers_plugin_directory', "$plugins_path/$path/wp-controllers", $path, $data);
169 | if ( is_dir($directory) ) {
170 | $this->trigger_deprecation_notice($path);
171 | $this->_directories[] = $directory;
172 | }
173 | }
174 | }
175 | }
176 |
177 | /**
178 | * @param string $source Plugin or theme the warning is for
179 | */
180 | private function trigger_deprecation_notice(string $source) {
181 | if ( defined('WP_DEBUG') && WP_DEBUG ) {
182 | trigger_error("Warning for $source: Auto-loading from WP Controllers will be deprecated by 1.0. We suggest adding your own PSR-4 auto-loading.", E_USER_WARNING);
183 | }
184 | }
185 | }
186 |
--------------------------------------------------------------------------------
/src/Plugin/PluginFactory.php:
--------------------------------------------------------------------------------
1 | controller
19 | * @var array $_controller_post_types post_type => controller
20 | */
21 | private static
22 | $_controller_templates = array(),
23 | $_controller_post_types = array();
24 |
25 | /**
26 | * @var object $post WP_Post class
27 | */
28 | protected
29 | $post;
30 |
31 | /**
32 | * @var object $post WP_Post class
33 | * @var int id
34 | * @var string slug
35 | * @var string title
36 | * @var string excerpt
37 | * @var string content
38 | * @var string status
39 | * @var string type
40 | * @var int author
41 | * @var int parent_id
42 | * @var int menu_order
43 | * @var int comment_count
44 | * @var string comment_status
45 | * @var string date
46 | * @var string date_gmt
47 | * @var string modified
48 | * @var string modified_gmt
49 | * @var string password
50 | * @var string to_ping
51 | * @var string pinged
52 | * @var string guide
53 | * @var string filter
54 | * @var string ping_status
55 | * @var string mime_type
56 | * @var string content_filtered
57 | * @var Meta meta
58 | */
59 | public
60 | $id,
61 | $slug,
62 | $title,
63 | $excerpt,
64 | $content,
65 | $status,
66 | $type,
67 | $author,
68 | $parent_id,
69 | $menu_order,
70 | $comment_count,
71 | $comment_status,
72 | $date,
73 | $date_gmt,
74 | $modified,
75 | $modified_gmt,
76 | $password,
77 | $to_ping,
78 | $pinged,
79 | $ping_status,
80 | $guid,
81 | $filter,
82 | $mime_type,
83 | $content_filtered,
84 | $meta;
85 |
86 | /**
87 | * Retrieves the controller for the post type
88 | * @since 0.7.0
89 | * @param string|int|object $key post id, slug, or WP_Post object
90 | * @param array $options
91 | * @return Post|WP_Error
92 | */
93 | public static function get_controller($key = null, $options = array()) {
94 | if ( is_null($key) ) {
95 | global $post;
96 | $key = $post;
97 | if ( empty($key) )
98 | return new WP_Error('no_global_post', 'Global post is null', $key);
99 | }
100 |
101 | // Check for cached object and set $post to the WP_Post otherwise
102 | if ( is_numeric($key) ) {
103 | $cached_post = wp_cache_get($key, 'postcontroller');
104 | if ( false !== $cached_post ) return $cached_post;
105 | $_post = get_post(intval($key));
106 | if ( !$_post ) return new WP_Error('post_id_not_found', 'No post was found for the provided ID', $key);
107 |
108 | } elseif ( is_string($key) ) {
109 | $post_id = wp_cache_get($key, 'postcontroller_slug');
110 | if ( false !== $post_id ) return wp_cache_get($post_id, 'postcontroller');
111 |
112 | $posts = get_posts(array(
113 | 'name' => $key,
114 | 'post_type' => empty($options['post_type']) ? 'any' : $options['post_type'],
115 | 'post_status' => array('publish', 'private'),
116 | 'numberposts' => 1
117 | ));
118 |
119 | if ( empty($posts) )
120 | return new WP_Error('post_slug_not_found', 'No post was found for the provided slug', $key);
121 |
122 | $_post = $posts[0];
123 |
124 | } elseif ( is_object($key) ) {
125 | $_post = wp_cache_get($key->ID, 'postcontroller');
126 | if ( false !== $_post ) return $_post;
127 |
128 | $_post = $key;
129 |
130 | } else {
131 | return new WP_Error('invalid_key_type', 'Key provied is unsupported type', $key);
132 | }
133 |
134 | // Construct, cache, and return post
135 | $controller_class = self::get_controller_class($_post, $options);
136 | $controller = new $controller_class ($_post);
137 |
138 | wp_cache_set($controller->slug, $controller->id, 'postcontroller_slug', MINUTE_IN_SECONDS * 10);
139 | wp_cache_set($controller->id, $controller, 'postcontroller', MINUTE_IN_SECONDS * 10);
140 |
141 | return $controller;
142 | }
143 |
144 | /**
145 | * Returns controllers for an array of posts or wp_query arguments
146 | * @since 0.7.0
147 | *
148 | * @param array $args Either an array of WP_Post objects or get_posts arguments
149 | * @param array $options Options to pass with the get_controllers function
150 | *
151 | * @return array
152 | */
153 | public static function get_controllers($args = null, $options = array()) {
154 | if ( is_null($args) ) {
155 | // Retrieve archive posts
156 | global $wp_query;
157 | if ( isset($wp_query->posts) ) {
158 | $posts = $wp_query->posts;
159 | } else {
160 | trigger_error('No posts found in the wp_query. Please use this only in a post archive template.', E_USER_WARNING);
161 | return array();
162 | }
163 |
164 | } elseif ( isset($args[0]) && ( is_object($args[0]) || is_numeric($args[0]) ) ) {
165 | // Turn array of WP_Posts into controllers
166 | $posts = $args;
167 |
168 | } elseif ( !empty($args) ) {
169 | // Retrieve posts from get_posts arguments
170 | if ( !isset($args['suppress_filters']) ) $args['suppress_filters'] = false;
171 | $posts = get_posts($args);
172 |
173 | if ( isset($args['fields']) && in_array($args['fields'], ['ids', 'id=>parent']) ) {
174 | return $posts;
175 | }
176 |
177 | } else {
178 | // Probably empty array of what would be posts or something unexpected
179 | return array();
180 | }
181 |
182 | $controllers = array();
183 | foreach($posts as $post) {
184 | $controllers[] = self::get_controller($post, $options);
185 | }
186 |
187 | return $controllers;
188 | }
189 |
190 | /**
191 | * Returns the class name for the corresponding post
192 | * @param WP_Post $post WP_Post to get the class for
193 | * @param array $options Options passed for the post
194 | * @return string Class name
195 | */
196 | public static function get_controller_class($post, $options = array()) {
197 | // Check for template-specific controller first
198 | $class = self::get_template_class($post);
199 | if ( $class ) return $class;
200 |
201 | // Set all the post type classes if not set
202 | if ( empty(self::$_controller_post_types) ) {
203 | self::set_post_type_controllers();
204 | }
205 |
206 | if ( 'attachment' === $post->post_type && wp_attachment_is_image($post) ) {
207 | $class = self::$_controller_post_types['image'];
208 | } else {
209 | $class = isset(self::$_controller_post_types[$post->post_type])
210 | ? self::$_controller_post_types[$post->post_type]
211 | : __CLASS__;
212 | }
213 |
214 | return apply_filters('wp_controllers_post_class', $class, $post, $options);
215 | }
216 |
217 | /**
218 | * Returns the post type name for a given class. If the class itself does not have an explicit post type, it will
219 | * traverse up inheritance until a class is found that is tied to a post type.
220 | *
221 | * @param string $class Class to look up
222 | * @param null $post_type_controllers Used for recursion
223 | * @param null $template_controllers Used for recursion
224 | *
225 | * @return string|false Post type name if found, otherwise false
226 | */
227 | public static function get_controller_post_type($class, $post_type_controllers = null, $template_controllers = null) {
228 | if ( !$class ) {
229 | return false;
230 | }
231 |
232 | if ( null === $post_type_controllers ) {
233 | $post_type_controllers = array_flip(self::$_controller_post_types);
234 | $template_controllers = array_flip(self::$_controller_templates);
235 | }
236 |
237 | if ( empty(self::$_controller_post_types) ) {
238 | self::set_post_type_controllers();
239 | }
240 |
241 | if ( isset($post_type_controllers[$class]) ) {
242 | return $post_type_controllers[$class];
243 | }
244 |
245 | if ( isset($template_controllers[$class]) ) {
246 | $parent_class = get_parent_class($class);
247 | return self::get_controller_post_type($parent_class, $post_type_controllers, $template_controllers);
248 | }
249 |
250 | return false;
251 | }
252 |
253 | /**
254 | * Maps the post types to controllers for later use
255 | */
256 | private static function set_post_type_controllers() {
257 | self::$_controller_post_types['image'] = 'Picture';
258 |
259 | $post_types = get_post_types(array(), 'objects');
260 | foreach($post_types as $type) {
261 | if ( !empty($type->wp_controller_class) ) {
262 | self::$_controller_post_types[$type->name] = $type->wp_controller_class;
263 | }
264 | }
265 | }
266 |
267 | /**
268 | * get_template_class
269 | * @param WP_Post $post post to retrieve the template class for
270 | * @return string|false class if there is one, false if not class or template
271 | */
272 | private static function get_template_class($post) {
273 | $template = get_page_template_slug($post);
274 | if ( !$template ) return false;
275 |
276 | if ( isset(self::$_controller_templates[$template]) ) {
277 | return self::$_controller_templates[$template];
278 |
279 | } else {
280 | $data = get_file_data(get_template_directory() . "/$template", array(
281 | 'controller_class' => 'WP Controller'
282 | ));
283 |
284 | $class = $data['controller_class'];
285 |
286 | if ( is_child_theme() ) {
287 | $data = get_file_data(get_stylesheet_directory() . "/$template", array(
288 | 'controller_class' => 'WP Controller'
289 | ));
290 |
291 | $class = !empty($data['controller_class']) ? $data['controller_class'] : $class;
292 | }
293 |
294 | return self::$_controller_templates[$template] = $class ? $class : false;
295 | }
296 | }
297 |
298 | /**
299 | * Called when the cache for a post controller needs to be flushed. Calls the flush_cache static
300 | * method for the class the post belongs to.
301 | * @param WP_Post $post post object that needs to be invalidated
302 | * @param string $event event which triggered the flush
303 | */
304 | public static function trigger_cache_flush($post, $event) {
305 | wp_cache_delete($post->post_name, 'postcontroller_slug');
306 | wp_cache_delete($post->ID, 'postcontroller');
307 |
308 | $controller_class = self::get_controller_class($post);
309 | if ( $controller_class && method_exists($controller_class, 'flush_cache') ) {
310 | $controller_class::flush_cache($post, $event);
311 | }
312 | }
313 |
314 | /**
315 | * Returns the meta class to be used for the post
316 | * @param WP_Post $post Post the meta class will be used for
317 | * @return string Meta class
318 | */
319 | protected static function get_meta_class($post) {
320 | return apply_filters('wp_controllers_meta_class', Meta::class, $post);
321 | }
322 |
323 | /**
324 | * Constructor.
325 | * Protect the constructor as we do not want the controllers
326 | * to be instantiated directly. This is to ensure caching.
327 | * @since 0.7.0
328 | * @param WP_Post $post
329 | */
330 | protected function __construct($post) {
331 | // Standard Properties
332 | $this->post = $post;
333 | $this->id = $this->post->ID;
334 | $this->slug = $this->post->post_name;
335 | $this->title = $this->post->post_title;
336 | $this->excerpt = $this->post->post_excerpt;
337 | $this->content = $this->post->post_content;
338 | $this->status = $this->post->post_status;
339 | $this->type = $this->post->post_type;
340 | $this->parent_id = $this->post->post_parent;
341 | $this->author = $this->post->post_author;
342 | $this->menu_order = $this->post->menu_order;
343 | $this->password = $this->post->post_password;
344 | $this->to_ping = $this->post->to_ping;
345 | $this->pinged = $this->post->pinged;
346 | $this->guid = $this->post->guid;
347 | $this->filter = $this->post->filter;
348 | $this->ping_status = $this->post->ping_status;
349 | $this->mime_type = $this->post->post_mime_type;
350 | $this->content_filtered = $this->post->post_content_filtered;
351 |
352 | // Comments
353 | $this->comment_count = $this->post->comment_count;
354 | $this->comment_status = $this->post->comment_status;
355 |
356 | // Dates
357 | $this->date = $this->post->post_date;
358 | $this->date_gmt = $this->post->post_date_gmt;
359 | $this->modified = $this->post->post_modified;
360 | $this->modified_gmt = $this->post->post_modified_gmt;
361 |
362 | // Meta class
363 | $meta_class = static::get_meta_class($post);
364 | $this->meta = new $meta_class($this->id, 'post');
365 | }
366 |
367 | public function __get($name) {
368 | if ( 'ID' === $name ) {
369 | return $this->id;
370 | }
371 |
372 | if ( 'post_name' === $name ) {
373 | return $this->slug;
374 | }
375 |
376 | if ( 'post_parent' === $name ) {
377 | return $this->parent_id;
378 | }
379 |
380 | if ( 0 === strpos($name, 'post_') ) {
381 | $short_key = substr($name, 5);
382 | if ( isset($this->$short_key) ) {
383 | return $this->$short_key;
384 | }
385 | }
386 |
387 | return null;
388 | }
389 |
390 | public function __isset($name) {
391 | if ( 0 === strpos($name, 'post_') ) {
392 | $short_key = substr($name, 5);
393 | return isset($this->$short_key);
394 | }
395 |
396 | return false;
397 | }
398 |
399 |
400 | /**
401 | * Returns adjacent post controller.
402 | *
403 | * @see https://codex.wordpress.org/Function_Reference/get_adjacent_post
404 | *
405 | * @param bool|false $same_term
406 | * @param array $excluded_terms
407 | * @param bool|true $previous
408 | * @param string $taxonomy
409 | *
410 | * @return Post|false Returns controller if available, and false if no post
411 | */
412 | protected function adjacent_post($same_term = false, $excluded_terms = array(), $previous = true, $taxonomy = 'category') {
413 | $date_type = $previous ? 'before' : 'after';
414 |
415 | $arguments = array(
416 | 'post_type' => $this->type,
417 | 'numberposts' => 1,
418 | 'order' => $previous ? 'DESC' : 'ASC',
419 | 'post__not_in'=> array($this->id),
420 | 'date_query' => array(
421 | array(
422 | $date_type => $this->date('F j, Y')
423 | )
424 | )
425 | );
426 |
427 | if ( $taxonomy && $same_term ) {
428 | $terms = get_terms($taxonomy, array(
429 | 'fields' => 'ids',
430 | 'exclude' => $excluded_terms
431 | ));
432 |
433 | if ( ! (empty($terms) || is_wp_error($terms) ) ) {
434 | $arguments['tax_query'] = array(
435 | array(
436 | 'taxonomy' => $taxonomy,
437 | 'field' => 'term_id',
438 | 'terms' => $terms
439 | )
440 | );
441 | }
442 | }
443 |
444 | $posts = get_posts($arguments);
445 | return isset($posts[0]) ? static::get_controller($posts[0]) : false;
446 | }
447 |
448 | /**
449 | * Returns post url
450 | * @return string
451 | */
452 | public function url() {
453 | return isset($this->_url) ? $this->_url : $this->_url = get_permalink($this->id);
454 | }
455 |
456 | /**
457 | * Returns archive url
458 | * @return string
459 | */
460 | public static function archive_url() {
461 | return get_post_type_archive_link(self::get_controller_post_type(get_called_class()));
462 | }
463 |
464 | /**
465 | * Returns the author User controller
466 | * @return User|WP_Error
467 | */
468 | public function author() {
469 | if ( isset($this->_author) ) return $this->_author;
470 | return $this->_author = get_user_controller($this->author);
471 | }
472 |
473 | /**
474 | * Returns timestamp or formatted date
475 | * @param string $format Date format
476 | * @param boolean $gmt whether to use gmt date
477 | * @return false|string
478 | */
479 | public function date($format, $gmt = false) {
480 | if ( 'timestamp' === $format ) {
481 | return $gmt ? strtotime($this->date_gmt) : strtotime($this->date);
482 | } else {
483 | return $gmt ? date($format, $this->date('timestamp', $this->date_gmt)) : date($format, $this->date('timestamp', $this->date));
484 | }
485 | }
486 |
487 | /**
488 | * Returns timestamp or formatted modified date
489 | * @param string $format Date format
490 | * @param boolean $gmt whether to use gmt date
491 | * @return false|string
492 | */
493 | public function modified($format, $gmt = false) {
494 | if ( 'timestamp' === $format ) {
495 | return $gmt ? strtotime($this->modified_gmt) : strtotime($this->modified);
496 | } else {
497 | return $gmt ? date($format, $this->date('timestamp', $this->modified_gmt)) : date($format, $this->date('timestamp', $this->modified));
498 | }
499 | }
500 |
501 | /**
502 | * Returns next post if available
503 | * @param boolean $same_term
504 | * @param array $excluded_terms
505 | * @param string $taxonomy
506 | * @return null|object
507 | */
508 | public function next_post($same_term = false, $excluded_terms = array(), $taxonomy = 'category') {
509 | return $this->adjacent_post($same_term, $excluded_terms, false, $taxonomy);
510 | }
511 |
512 | /**
513 | * Returns previous post if available
514 | * @param boolean $same_term
515 | * @param array $excluded_terms
516 | * @param string $taxonomy
517 | * @return null|object
518 | */
519 | public function previous_post($same_term = false, $excluded_terms = array(), $taxonomy = 'category') {
520 | return $this->adjacent_post($same_term, $excluded_terms, true, $taxonomy);
521 | }
522 |
523 | /**
524 | * Retrieves taxonomy terms and can apply urls
525 | * @since 0.1.0
526 | * @param string|array $taxonomies
527 | * @return array|WP_Error
528 | */
529 | public function terms($taxonomies) {
530 | // Retrieve terms
531 | $terms = wp_get_post_terms($this->id, $taxonomies);
532 |
533 | if ( is_wp_error($terms) ) return $terms;
534 |
535 | $controllers = array();
536 | foreach($terms as $term) {
537 | $controllers[] = Term::get_controller($term);
538 | }
539 |
540 | return $controllers;
541 | }
542 |
543 | /**
544 | * Returns array of posts that share taxonomy
545 | * @since 0.1.0
546 | * @param string $post_type
547 | * @param string|array $taxonomies
548 | * @param array $options (optional)
549 | * @return array|null of controllers
550 | */
551 | public function related_posts($post_type, $taxonomies, $options = null) {
552 | $options = wp_parse_args( $options, array(
553 | 'count' => -1,
554 | 'meta_query'=> null
555 | ));
556 |
557 | $args = array(
558 | 'post_type' => $post_type,
559 | 'posts_per_page'=> $options['count'],
560 | 'post__not_in' => array($this->id),
561 | 'tax_query' => array(
562 | 'relation' => 'OR',
563 | )
564 | );
565 |
566 | if ( $options['meta_query'] ) {
567 | $args['meta_query'] = $options['meta_query'];
568 | }
569 |
570 | if ( !is_array($taxonomies) )
571 | $taxonomies = array($taxonomies);
572 |
573 | foreach($taxonomies as $index => $taxonomy) {
574 | // Retrieve terms and continue if empty
575 | $terms = $this->terms($taxonomy);
576 | if ( is_null($terms) ) continue;
577 |
578 | // Store the ids into an array
579 | $term_ids = array();
580 | foreach($terms as $term)
581 | $term_ids[] = $term->term_id;
582 |
583 | $args['tax_query'][] = array(
584 | 'taxonomy' => $taxonomy,
585 | 'field' => 'id',
586 | 'terms' => $term_ids
587 | );
588 | }
589 |
590 | if ( count($args['tax_query']) === 1 )
591 | return null;
592 |
593 | return static::get_controllers($args);
594 | }
595 |
596 | /**
597 | * Returns the featured image
598 | * @since 0.1.0
599 | * @param array $options (optional)
600 | * @return object|null
601 | */
602 | public function featured_image($options = array()) {
603 | $id = $this->meta->_thumbnail_id('single');
604 | return $id ? static::get_controller( $id, $options ) : null;
605 | }
606 |
607 | /**
608 | * Returns whether or not the given term(s) are associated with the post. If no terms are provided
609 | * then whether or not the post has any terms within the taxonomy
610 | * @param string $taxonomy Single taxonomy name
611 | * @param int|string|array $term Optional. Term term_id, name, slug or array of said. Default null.
612 | * @return boolean
613 | */
614 | public function has_term($taxonomy, $term = null) {
615 | $result = is_object_in_term($this->id, $taxonomy, $term);
616 | return $result && !is_wp_error($result);
617 | }
618 |
619 | /**
620 | * Returns the content with standard filters applied
621 | * if password is required it returns the form
622 | * @return string
623 | */
624 | public function content() {
625 | if ( $this->password_required() ) {
626 | return get_the_password_form($this->post);
627 | } else {
628 | return apply_filters('the_content', $this->content);
629 | }
630 | }
631 |
632 | /**
633 | * Returns true if the post is password protected and the password is still required
634 | * @return boolean
635 | */
636 | public function password_required() {
637 | return post_password_required($this->post);
638 | }
639 |
640 | /**
641 | * Returns the title with the WP filters applied
642 | * @return string
643 | */
644 | public function title() {
645 | return apply_filters('the_title', $this->title, $this->id, $this);
646 | }
647 |
648 | /**
649 | * Returns the filtered excpert or limited content if no filter exists
650 | * @since 0.1.0
651 | * @param int $word_count (default: 40)
652 | * @param string $ellipsis (default: '...')
653 | * @param boolean $apply_filters (default: true)
654 | * @return string
655 | */
656 | public function excerpt($word_count = 40, $ellipsis = '...', $apply_filters = true) {
657 | if ( !empty($this->excerpt) )
658 | return ($apply_filters) ? apply_filters('the_excerpt', $this->excerpt) : $this->excerpt;
659 |
660 | if ( $apply_filters ) {
661 | remove_filter('the_excerpt', 'wpautop');
662 | $content = wp_kses($this->content, array(
663 | 'em' => array(),
664 | 'strong'=> array(),
665 | 'u' => array(),
666 | 'a' => array(
667 | 'href' => array(),
668 | 'title' => array()
669 | )
670 | ));
671 | $content = strip_shortcodes($content);
672 | $content = apply_filters('the_excerpt', $content);
673 | } else
674 | $content = $this->content;
675 |
676 | return $word_count ? wp_trim_words($content, $word_count, $ellipsis) : $content;
677 | }
678 |
679 | /**
680 | * Returns post controllers organized by terms
681 | * @since 0.1.0
682 | * @param string $taxonomy
683 | * @return array
684 | */
685 | protected static function get_categorized_posts($taxonomy) {
686 | $categorized_posts = array();
687 | $terms = get_terms($taxonomy);
688 |
689 | foreach($terms as $term) {
690 | $posts = get_posts(array(
691 | 'post_type' => self::get_controller_post_type(get_called_class()),
692 | 'numberposts' => -1,
693 | 'tax_query' => array(
694 | array(
695 | 'taxonomy' => $taxonomy,
696 | 'field' => 'id',
697 | 'terms' => $term->term_id
698 | )
699 | )
700 | ));
701 |
702 | $term->posts = array();
703 | foreach($posts as $post)
704 | $term->posts[] = static::get_controller($post);
705 |
706 | $categorized_posts[] = $term;
707 | }
708 |
709 | return $categorized_posts;
710 | }
711 |
712 | /**
713 | * Returns the most recent n posts
714 | * @since 0.1.0
715 | * @param int $numberposts
716 | * @param boolean|array $exclude excludes current post if true
717 | * @return array
718 | */
719 | public static function get_recent($numberposts, $exclude = true) {
720 | if ( !is_array($exclude) ) {
721 | if ( true === $exclude ) {
722 | $id = get_the_ID();
723 | if ( false !== $id )
724 | $exclude = array($id);
725 | } else
726 | $exclude = array();
727 | }
728 |
729 | return static::get_controllers(array(
730 | 'post_type' => self::get_controller_post_type(get_called_class()),
731 | 'numberposts' => $numberposts,
732 | 'post__not_in'=> $exclude
733 | ));
734 | }
735 |
736 | /**
737 | * Retrieves random posts
738 | *
739 | * Retrieves a number of randomized posts. Use this instead of the
740 | * ORDER BY RAND method, as that can have tremendous overhead
741 | * @since 0.5.0
742 | * @param int $count the number of posts to return randomized
743 | * @param array|string $post_type (optional) post type name or array thereof
744 | * @return array array of controllers or empty array
745 | */
746 | public static function random_posts($count = -1, $post_type = null) {
747 | $post_type = ( $post_type ) ? $post_type : self::get_controller_post_type(get_called_class());
748 |
749 | $ids = get_posts(array(
750 | 'post_type' => $post_type,
751 | 'numberposts' => -1,
752 | 'fields' => 'ids'
753 | ));
754 |
755 | if ( empty($ids) ) return array();
756 |
757 | shuffle($ids);
758 |
759 | if ( $count !== -1 )
760 | $ids = array_slice($ids, 0, $count);
761 |
762 | return static::get_controllers(array(
763 | 'post_type' => $post_type,
764 | 'numberposts' => $count,
765 | 'post__in' => $ids
766 | ));
767 | }
768 |
769 | /**
770 | * Returns whether provided id belongs to post type
771 | * @param integer $id
772 | * @param string $type
773 | * @return boolean
774 | */
775 | public static function is_post($id, $type = 'any') {
776 | $args = array(
777 | 'suppress_filters'=> false
778 | ,'post_type' => $type
779 | ,'fields' => 'ids'
780 | ,'posts_per_page' => 1
781 | );
782 |
783 | if ( is_numeric($id) ) {
784 | $args['post__in'] = array($id);
785 | } else {
786 | $args['name'] = $id;
787 | }
788 |
789 | return ( !empty(get_posts($args)) );
790 | }
791 | };
792 |
--------------------------------------------------------------------------------
/src/Term.php:
--------------------------------------------------------------------------------
1 | term_id, self::CACHE_GROUP);
71 | if ( false !== $term ) return $term;
72 | $term = $key;
73 |
74 | } elseif ( $key ) {
75 | if ( $field == 'id' ) {
76 | $term = wp_cache_get($key, self::CACHE_GROUP);
77 | if ( false !== $term ) return $term;
78 | } else {
79 | $term_id = wp_cache_get($key, self::CACHE_GROUP . '_' . $field);
80 | if ( false !== $term_id )
81 | return wp_cache_get($term_id, self::CACHE_GROUP);
82 | }
83 | $term = get_term_by($field, $key, $taxonomy);
84 |
85 | } else {
86 | $term = get_queried_object();
87 | if ( !isset($term->term_id) )
88 | return new WP_Error('invalid_queried_object', 'The queried object is not a term', $term);
89 |
90 | $controller = wp_cache_get($term->term_id, self::CACHE_GROUP);
91 | if ( false !== $controller ) return $controller;
92 | }
93 |
94 | if ( false === $term ) {
95 | return $term;
96 | }
97 |
98 | // Construct, cache, and return term
99 | $controller_class = self::get_controller_class($term, $options);
100 | $controller = new $controller_class ($term);
101 |
102 | wp_cache_set($controller->id, $controller, self::CACHE_GROUP, MINUTE_IN_SECONDS * 10);
103 | wp_cache_set($controller->slug, $controller->id, self::CACHE_GROUP . '_' . 'slug', MINUTE_IN_SECONDS * 10);
104 | wp_cache_set($controller->name, $controller->id, self::CACHE_GROUP . '_' . 'name', MINUTE_IN_SECONDS * 10);
105 | wp_cache_set($controller->taxonomy_id, $controller->id, self::CACHE_GROUP . '_' . 'term_taxonomy_id', MINUTE_IN_SECONDS * 10);
106 |
107 | return $controller;
108 | }
109 |
110 | /**
111 | * Returns an array of Term controllers from either arguments for an array of ids. If an array of
112 | * ids is supplied, the second parameter is the taxonomy the ids belong to
113 | * @param array $args get_terms arguments or array of ids
114 | * @param string $taxonomy taxonomy for array of ids
115 | * @param array $options Controller options
116 | * @return array array of controllers
117 | */
118 | public static function get_controllers($args, $taxonomy = '', $options = array()) {
119 | if ( isset($args[0]) || empty($args) ) {
120 | $terms = $args;
121 | } else {
122 | $terms = get_terms($args);
123 |
124 | if ( isset($args['fields']) && 'all' !== $args['fields'] ) {
125 | return $terms;
126 | }
127 | }
128 |
129 | $Terms = array();
130 | foreach($terms as $term) {
131 | $Terms[] = self::get_controller($term, $taxonomy, $options);
132 | }
133 |
134 | return $Terms;
135 | }
136 |
137 | /**
138 | * Returns the class name for the corresponding term
139 | * @param WP_Term $term WP_Term to get the class for
140 | * @param array $options Array of options
141 | * @return string Class name
142 | */
143 | private static function get_controller_class($term, $options = array()) {
144 | if ( !is_array(self::$_controller_taxonomies) ) {
145 | self::$_controller_taxonomies = array();
146 |
147 | $taxonomies = get_taxonomies(array(), 'objects');
148 | foreach($taxonomies as $taxonomy) {
149 | if ( !empty($taxonomy->wp_controller_class) ) {
150 | self::$_controller_taxonomies[$taxonomy->name] = $taxonomy->wp_controller_class;
151 | }
152 | }
153 | }
154 |
155 | $class = isset(self::$_controller_taxonomies[$term->taxonomy])
156 | ? self::$_controller_taxonomies[$term->taxonomy]
157 | : __CLASS__;
158 |
159 | return apply_filters('wp_controllers_term_class', $class, $term, $options);
160 | }
161 |
162 | /**
163 | * Called when the cache for a term controller needs to be flushed. Calls the flush_cache static
164 | * method for the class the term belongs to.
165 | * @param WP_Term $term term object that needs to be invalidated
166 | * @param string $event event which triggered the flush
167 | */
168 | public static function trigger_flush_cache($term, $event) {
169 | wp_cache_delete($term->term_id, self::CACHE_GROUP);
170 | wp_cache_delete($term->slug, self::CACHE_GROUP . '_slug');
171 | wp_cache_delete($term->name, self::CACHE_GROUP . '_name');
172 | wp_cache_delete($term->term_taxonomy_id, self::CACHE_GROUP . '_term_taxonomy_id');
173 |
174 | $controller_class = self::get_controller_class($term);
175 | if ( $controller_class && method_exists($controller_class, 'flush_cache') ) {
176 | $controller_class::flush_cache($term, $event);
177 | }
178 | }
179 |
180 | /**
181 | * Returns the meta class to be used for the term
182 | * @param WP_Term $term Term the meta class will be used for
183 | * @return string Meta class
184 | */
185 | protected static function get_meta_class($term) {
186 | return apply_filters('wp_controllers_term_meta_class', Meta::class, $term);
187 | }
188 |
189 | /**
190 | * Term constructor.
191 | *
192 | * @param object $term
193 | */
194 | protected function __construct($term) {
195 | // Load all the term properties
196 | foreach(get_object_vars($term) as $key => $value)
197 | $this->$key = $value;
198 |
199 | // Extra properties
200 | $this->term =& $term;
201 | $this->id =& $term->term_id;
202 | $this->group =& $term->term_group;
203 | $this->taxonomy_id =& $term->term_taxonomy_id;
204 |
205 | // Meta class
206 | $meta_class = static::get_meta_class($term);
207 | $this->meta = new $meta_class($this->id, 'post');
208 | }
209 |
210 | /**
211 | * Returns the term url
212 | * @return string|WP_Error
213 | */
214 | public function url() {
215 | return isset($this->url) ? $this->url : ( $this->url = get_term_link($this->term) );
216 | }
217 |
218 | /**
219 | * Returns the term name filtered by the standard filters
220 | * @return string filtered term name
221 | */
222 | public function title() {
223 | switch($this->taxonomy) {
224 | case 'category': return apply_filters('single_cat_title', $this->name);
225 | case 'post_tag': return apply_filters('single_tag_title', $this->name);
226 | default: return apply_filters('single_term_title', $this->name);
227 | }
228 | }
229 |
230 | /**
231 | * Returns the parent term controller if there is one
232 | * @return Term|null controller if has parent
233 | */
234 | public function parent() {
235 | return $this->parent ? self::get_controller($this->parent, $this->taxonomy, 'id') : null;
236 | }
237 |
238 | /**
239 | * Returns the children term controllers
240 | * @return array child term controllers
241 | */
242 | public function children() {
243 | return self::get_controllers(array(
244 | 'taxonomy' => $this->taxonomy,
245 | 'child_of' => $this->id
246 | ));
247 | }
248 |
249 | /**
250 | * Returns the term description filtered by the_content
251 | * @return string filtered description
252 | */
253 | public function description() {
254 | return apply_filters('the_content', $this->description);
255 | }
256 |
257 | /**
258 | * Returns the posts that have this term
259 | * @param string $post_type post type(s) to limit the query to; default: any
260 | * @param integer $count the number of posts to return; default: -1
261 | * @return Post[] Post controllers
262 | */
263 | public function posts($post_type = 'any', $count = -1) {
264 | if ( !$this->count ) return array();
265 |
266 | return Post::get_controllers(array(
267 | 'post_type' => $post_type,
268 | 'numberposts' => $count,
269 | 'tax_query' => array(
270 | array(
271 | 'taxonomy' => $this->taxonomy,
272 | 'terms' => $this->id
273 | )
274 | )
275 | ));
276 | }
277 |
278 | /**
279 | * @param $post_type
280 | * @return Post|null
281 | */
282 | public function oldest_post($post_type) {
283 | if ( !$this->count ) return null;
284 |
285 | $Post = get_post_controllers(array(
286 | 'post_type' => $post_type,
287 | 'numberposts' => 1,
288 | 'orderby' => 'date',
289 | 'order' => 'ASC',
290 | 'tax_query' => array(
291 | array(
292 | 'taxonomy' => $this->taxonomy,
293 | 'terms' => $this->id
294 | )
295 | )
296 | ));
297 |
298 | return empty($Post) ? $Post[0] : null;
299 | }
300 |
301 | /**
302 | * Returns the distinct terms for an array of posts
303 | *
304 | * @param array $posts
305 | * @param string $fields
306 | *
307 | * @return array|null|object
308 | */
309 | public static function distinct_post_terms(array $posts, $fields = '') {
310 | $ids = array();
311 | foreach($posts as $post) {
312 | if ( is_numeric($post) )
313 | $ids[] = absint($post);
314 | elseif ( is_a($post, 'WP_Post') )
315 | $ids[] = $post->ID;
316 | elseif ( is_a($post, 'Post') )
317 | $ids[] = $post->id;
318 | }
319 |
320 | if ( empty($ids) ) return array();
321 |
322 | global $wpdb;
323 | $ids = array_map( 'intval', $ids );
324 | $ids = implode(',', $ids);
325 |
326 | $query = "
327 | SELECT DISTINCT t.term_id, t.name, t.slug, t.term_group
328 |
329 | FROM `wp_terms` as t
330 | JOIN `wp_term_taxonomy` as tax ON t.term_id = tax.term_id
331 | JOIN `wp_term_relationships` as rel ON tax.term_taxonomy_id = rel.term_taxonomy_id
332 |
333 | WHERE rel.object_id IN ($ids);
334 | ";
335 |
336 |
337 | switch($fields) {
338 | case 'raw':
339 | return $wpdb->get_results($query, OBJECT_K);
340 | case 'ids':
341 | return $wpdb->get_col($query);
342 | default:
343 | $results = $wpdb->get_results($query);
344 | foreach($results as &$result)
345 | $result = self::get_controller($result);
346 | return $results;
347 | }
348 | }
349 | };
350 |
--------------------------------------------------------------------------------
/src/User.php:
--------------------------------------------------------------------------------
1 | true
72 | ));
73 |
74 | if ( is_object($key) ) {
75 | $user = wp_cache_get($key->ID, self::CACHE_GROUP);
76 | if ( false !== $user ) return $user;
77 | $user = $key;
78 |
79 | } elseif ( $key ) {
80 | // Retrieve user and check if cached
81 | if ( $field == 'id' ) {
82 | $user = wp_cache_get($key, self::CACHE_GROUP);
83 | if ( false !== $user ) return $user;
84 |
85 | } else {
86 | $user_id = wp_cache_get("{$key}_{$field}", self::CACHE_GROUP);
87 | if ( false !== $user_id )
88 | return wp_cache_get($user_id, self::CACHE_GROUP);
89 | }
90 |
91 | $user = get_user_by($field, $key);
92 |
93 | if ( false === $user )
94 | return new WP_Error('user_not_found', 'No user was found with the provided parameters', array('field' => $field, 'value' => $key));
95 |
96 | } else {
97 | if ( !is_user_logged_in() )
98 | return new WP_Error('user_not_logged_in', 'No user is logged in to return');
99 |
100 | $user = wp_get_current_user();
101 |
102 | $controller = wp_cache_get($user->ID, self::CACHE_GROUP);
103 | if ( false !== $controller ) return $controller;
104 | }
105 |
106 | $controller_class = self::get_controller_class($user);
107 | $controller = new $controller_class ($user, $options['load_standard_meta']);
108 |
109 | wp_cache_set($controller->id, $controller, self::CACHE_GROUP, MINUTE_IN_SECONDS * 10);
110 | wp_cache_set($controller->email, $controller->id, self::CACHE_GROUP . '_email', MINUTE_IN_SECONDS * 10);
111 | wp_cache_set($controller->nice_name, $controller->id, self::CACHE_GROUP . '_slug', MINUTE_IN_SECONDS * 10);
112 | wp_cache_set($controller->login, $controller->id, self::CACHE_GROUP . '_login', MINUTE_IN_SECONDS * 10);
113 |
114 | return $controller;
115 | }
116 |
117 | /**
118 | * Returns controllers for an array of users or wp_user_query arguments
119 | * @param array $args
120 | * @return array
121 | */
122 | public static function get_controllers($args) {
123 | if ( isset($args[0]) && ( is_object($args[0]) || is_numeric($args[0]) ) ) {
124 | // Turn array of WP_User or id's into controllers
125 | $users = $args;
126 |
127 | } elseif ( !empty($args) ) {
128 | // Retrieve users via get_users function
129 | $users = get_users($args);
130 |
131 | } else {
132 | // Just return empty
133 | return array();
134 | }
135 |
136 | $controllers = array();
137 | foreach($users as $user) {
138 | $controllers[] = self::get_controller($user);
139 | }
140 |
141 | return $controllers;
142 | }
143 |
144 | /**
145 | * Retrieves the controller class for the WP_User instance
146 | * @param WP_User $user WP_User the controller is for
147 | * @return string Fully qualified controller class
148 | */
149 | private static function get_controller_class($user) {
150 | return apply_filters('wp_controllers_user_class', __CLASS__, $user);
151 | }
152 |
153 | /**
154 | * Called when the cache for a user controller needs to be flushed. Calls the flush_cache static
155 | * method for the class the user belongs to.
156 | * @param WP_User $user user object that needs to be invalidated
157 | * @param string $event event which triggered the flush
158 | */
159 | public static function trigger_flush_cache($user, $event) {
160 | wp_cache_delete($user->ID, self::CACHE_GROUP);
161 | wp_cache_delete($user->data->user_email, self::CACHE_GROUP . '_email');
162 | wp_cache_delete($user->data->user_nicename, self::CACHE_GROUP . '_slug');
163 | wp_cache_delete($user->data->user_login, self::CACHE_GROUP . '_login');
164 | }
165 |
166 | /**
167 | * User constructor.
168 | *
169 | * @param WP_User $user
170 | * @param bool $load_extra
171 | */
172 | protected function __construct($user, $load_extra) {
173 | $this->user = $user;
174 |
175 | $this->id =& $user->ID;
176 | $this->capabilities =& $user->caps;
177 | $this->roles =& $user->roles;
178 | $this->all_capabilities =& $user->allcaps;
179 |
180 | $this->display_name =& $user->data->display_name;
181 | $this->nice_name =& $user->data->user_nicename;
182 |
183 | $this->login =& $user->data->user_login;
184 | $this->email =& $user->data->user_email;
185 | $this->status =& $user->data->user_status;
186 | $this->registered =& $user->data->user_registered;
187 |
188 | if ( $load_extra ) {
189 | $this->first_name = $user->first_name;
190 | $this->last_name = $user->last_name;
191 | $this->description = $user->description;
192 | }
193 |
194 | // Meta class
195 | $this->meta = new Meta($this->id, 'user');
196 | }
197 |
198 | /**
199 | * @param string $format
200 | *
201 | * @return bool|int|string
202 | */
203 | public function registered($format) {
204 | return ( 'timestamp' === $format )
205 | ? strtotime($this->registered)
206 | : date($format, strtotime($this->registered));
207 | }
208 |
209 | /**
210 | * @return string
211 | */
212 | public function posts_url() {
213 | return get_author_posts_url($this->id);
214 | }
215 |
216 | /**
217 | * @param string[]|string|null $post_types
218 | *
219 | * @return User[]
220 | */
221 | public static function get_authors($post_types = null) {
222 | global $wpdb;
223 |
224 | if ( is_array($post_types) ) {
225 | $place_holders = implode(',', array_fill(0, count($post_types), '%s'));
226 | $where = $wpdb->prepare("AND P.post_type IN ($place_holders)", $post_types);
227 | } else if ( $post_types ) {
228 | $where = $wpdb->prepare("AND P.post_type = %s", $post_types);
229 | } else
230 | $where = '';
231 |
232 | $author_ids = $wpdb->get_col("
233 | SELECT U.ID
234 |
235 | FROM $wpdb->users AS U
236 | JOIN $wpdb->posts AS P ON U.ID = P.post_author
237 |
238 | WHERE P.post_status = 'publish'
239 | $where
240 |
241 | GROUP BY U.ID;
242 | ");
243 |
244 | $authors = array();
245 | foreach($author_ids as $index => $id)
246 | $authors[] = self::get_controller($id);
247 |
248 | return $authors;
249 | }
250 |
251 | /**
252 | * @param int[] $user_ids
253 | * @param null $post_type
254 | *
255 | * @return array
256 | */
257 | public static function get_users_post_count($user_ids, $post_type = null) {
258 | $user_ids = ( $user_ids ) ? $user_ids : get_users();
259 | $post_type = ( $post_type ) ? $post_type : get_post_types();
260 |
261 | if ( is_array($post_type) && (count($post_type) > 1) ) {
262 | $results = array();
263 | foreach($post_type as $type) {
264 | $counts = count_many_users_posts($user_ids, $type);
265 |
266 | foreach($counts as $user_id => $count)
267 | $results[$user_id][$type] = (integer) $count;
268 | }
269 |
270 | return $results;
271 |
272 | } else {
273 | $post_type = ( is_array($post_type) ) ? $post_type[0] : $post_type;
274 | return count_many_users_posts($user_ids, $post_type);
275 | }
276 | }
277 | };
278 |
--------------------------------------------------------------------------------
/tests/bin/install-wp-tests.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | if [ $# -lt 3 ]; then
4 | echo "usage: $0 [db-host] [wp-version] [skip-database-creation]"
5 | exit 1
6 | fi
7 |
8 | DB_NAME=$1
9 | DB_USER=$2
10 | DB_PASS=$3
11 | DB_HOST=${4-localhost}
12 | WP_VERSION=${5-latest}
13 | SKIP_DB_CREATE=${6-false}
14 |
15 | TMPDIR=${TMPDIR-/tmp}
16 | TMPDIR=$(echo $TMPDIR | sed -e "s/\/$//")
17 | WP_TESTS_DIR=${WP_TESTS_DIR-$TMPDIR/wordpress-tests-lib}
18 | WP_CORE_DIR=${WP_CORE_DIR-$TMPDIR/wordpress/}
19 |
20 | download() {
21 | if [ `which curl` ]; then
22 | curl -s "$1" > "$2";
23 | elif [ `which wget` ]; then
24 | wget -nv -O "$2" "$1"
25 | fi
26 | }
27 |
28 | if [[ $WP_VERSION =~ ^[0-9]+\.[0-9]+$ ]]; then
29 | WP_TESTS_TAG="branches/$WP_VERSION"
30 | elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0-9]+ ]]; then
31 | if [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0] ]]; then
32 | # version x.x.0 means the first release of the major version, so strip off the .0 and download version x.x
33 | WP_TESTS_TAG="tags/${WP_VERSION%??}"
34 | else
35 | WP_TESTS_TAG="tags/$WP_VERSION"
36 | fi
37 | elif [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then
38 | WP_TESTS_TAG="trunk"
39 | else
40 | # http serves a single offer, whereas https serves multiple. we only want one
41 | download http://api.wordpress.org/core/version-check/1.7/ /tmp/wp-latest.json
42 | grep '[0-9]+\.[0-9]+(\.[0-9]+)?' /tmp/wp-latest.json
43 | LATEST_VERSION=$(grep -o '"version":"[^"]*' /tmp/wp-latest.json | sed 's/"version":"//')
44 | if [[ -z "$LATEST_VERSION" ]]; then
45 | echo "Latest WordPress version could not be found"
46 | exit 1
47 | fi
48 | WP_TESTS_TAG="tags/$LATEST_VERSION"
49 | fi
50 |
51 | set -ex
52 |
53 | install_wp() {
54 |
55 | if [ -d $WP_CORE_DIR ]; then
56 | return;
57 | fi
58 |
59 | mkdir -p $WP_CORE_DIR
60 |
61 | if [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then
62 | mkdir -p $TMPDIR/wordpress-nightly
63 | download https://wordpress.org/nightly-builds/wordpress-latest.zip $TMPDIR/wordpress-nightly/wordpress-nightly.zip
64 | unzip -q $TMPDIR/wordpress-nightly/wordpress-nightly.zip -d $TMPDIR/wordpress-nightly/
65 | mv $TMPDIR/wordpress-nightly/wordpress/* $WP_CORE_DIR
66 | else
67 | if [ $WP_VERSION == 'latest' ]; then
68 | local ARCHIVE_NAME='latest'
69 | elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+ ]]; then
70 | # https serves multiple offers, whereas http serves single.
71 | download https://api.wordpress.org/core/version-check/1.7/ $TMPDIR/wp-latest.json
72 | if [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0] ]]; then
73 | # version x.x.0 means the first release of the major version, so strip off the .0 and download version x.x
74 | LATEST_VERSION=${WP_VERSION%??}
75 | else
76 | # otherwise, scan the releases and get the most up to date minor version of the major release
77 | local VERSION_ESCAPED=`echo $WP_VERSION | sed 's/\./\\\\./g'`
78 | LATEST_VERSION=$(grep -o '"version":"'$VERSION_ESCAPED'[^"]*' $TMPDIR/wp-latest.json | sed 's/"version":"//' | head -1)
79 | fi
80 | if [[ -z "$LATEST_VERSION" ]]; then
81 | local ARCHIVE_NAME="wordpress-$WP_VERSION"
82 | else
83 | local ARCHIVE_NAME="wordpress-$LATEST_VERSION"
84 | fi
85 | else
86 | local ARCHIVE_NAME="wordpress-$WP_VERSION"
87 | fi
88 | download https://wordpress.org/${ARCHIVE_NAME}.tar.gz $TMPDIR/wordpress.tar.gz
89 | tar --strip-components=1 -zxmf $TMPDIR/wordpress.tar.gz -C $WP_CORE_DIR
90 | fi
91 |
92 | download https://raw.github.com/markoheijnen/wp-mysqli/master/db.php $WP_CORE_DIR/wp-content/db.php
93 | }
94 |
95 | install_test_suite() {
96 | # portable in-place argument for both GNU sed and Mac OSX sed
97 | if [[ $(uname -s) == 'Darwin' ]]; then
98 | local ioption='-i .bak'
99 | else
100 | local ioption='-i'
101 | fi
102 |
103 | # set up testing suite if it doesn't yet exist
104 | if [ ! -d $WP_TESTS_DIR ]; then
105 | # set up testing suite
106 | mkdir -p $WP_TESTS_DIR
107 | svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/includes/ $WP_TESTS_DIR/includes
108 | svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/data/ $WP_TESTS_DIR/data
109 | fi
110 |
111 | if [ ! -f wp-tests-config.php ]; then
112 | download https://develop.svn.wordpress.org/${WP_TESTS_TAG}/wp-tests-config-sample.php "$WP_TESTS_DIR"/wp-tests-config.php
113 | # remove all forward slashes in the end
114 | WP_CORE_DIR=$(echo $WP_CORE_DIR | sed "s:/\+$::")
115 | sed $ioption "s:dirname( __FILE__ ) . '/src/':'$WP_CORE_DIR/':" "$WP_TESTS_DIR"/wp-tests-config.php
116 | sed $ioption "s/youremptytestdbnamehere/$DB_NAME/" "$WP_TESTS_DIR"/wp-tests-config.php
117 | sed $ioption "s/yourusernamehere/$DB_USER/" "$WP_TESTS_DIR"/wp-tests-config.php
118 | sed $ioption "s/yourpasswordhere/$DB_PASS/" "$WP_TESTS_DIR"/wp-tests-config.php
119 | sed $ioption "s|localhost|${DB_HOST}|" "$WP_TESTS_DIR"/wp-tests-config.php
120 | fi
121 |
122 | }
123 |
124 | install_db() {
125 |
126 | if [ ${SKIP_DB_CREATE} = "true" ]; then
127 | return 0
128 | fi
129 |
130 | # parse DB_HOST for port or socket references
131 | local PARTS=(${DB_HOST//\:/ })
132 | local DB_HOSTNAME=${PARTS[0]};
133 | local DB_SOCK_OR_PORT=${PARTS[1]};
134 | local EXTRA=""
135 |
136 | if ! [ -z $DB_HOSTNAME ] ; then
137 | if [ $(echo $DB_SOCK_OR_PORT | grep -e '^[0-9]\{1,\}$') ]; then
138 | EXTRA=" --host=$DB_HOSTNAME --port=$DB_SOCK_OR_PORT --protocol=tcp"
139 | elif ! [ -z $DB_SOCK_OR_PORT ] ; then
140 | EXTRA=" --socket=$DB_SOCK_OR_PORT"
141 | elif ! [ -z $DB_HOSTNAME ] ; then
142 | EXTRA=" --host=$DB_HOSTNAME --protocol=tcp"
143 | fi
144 | fi
145 |
146 | # create database
147 | mysqladmin create $DB_NAME --user="$DB_USER" --password="$DB_PASS"$EXTRA
148 | }
149 |
150 | install_wp
151 | install_test_suite
152 | install_db
153 |
--------------------------------------------------------------------------------
/tests/bootstrap.php:
--------------------------------------------------------------------------------
1 | factory()->post->create_and_get();
8 |
9 | // Get controller by WP_Post
10 | $controller = Post::get_controller($post);
11 | $this->assertInstanceOf(Post::class, $controller);
12 |
13 | // Get controller by id
14 | $controller = Post::get_controller($post->ID);
15 | $this->assertInstanceOf(Post::class, $controller);
16 |
17 | // Get controller by slug
18 | $controller = Post::get_controller($post->post_name);
19 | $this->assertInstanceOf(Post::class, $controller);
20 |
21 | // Get controller by single template
22 | $this->go_to("/p={$post->ID}");
23 | $controller = Post::get_controller();
24 | $this->assertInstanceOf(Post::class, $controller);
25 | $this->assertEquals($post->ID, $controller->id);
26 | }
27 |
28 | public function test_get_controllers() {
29 | $posts = $this->factory()->post->create_many(2);
30 |
31 | // Array of posts
32 | $controllers = Post::get_controllers($posts);
33 | $this->assertEquals($posts, wp_list_pluck($controllers, 'id'));
34 |
35 | // Query
36 | $controllers = Post::get_controllers([
37 | 'post_type' => 'post',
38 | 'order' => 'ASC'
39 | ]);
40 | $this->assertEquals($posts, wp_list_pluck($controllers, 'id'));
41 |
42 | // Something unexpected
43 | $controllers = Post::get_controllers(false);
44 | $this->assertEmpty($controllers);
45 |
46 | // Get controllers by posts archive
47 | $this->go_to(get_post_type_archive_link('post'));
48 | $controllers = Post::get_controllers();
49 | $controller_ids = wp_list_pluck($controllers, 'id');
50 | sort($posts);
51 | sort($controller_ids);
52 | $this->assertEquals($posts, $controller_ids);
53 | }
54 |
55 | public function test_get_controller_post_type() {
56 | // Root level post
57 | $this->assertSame('post', Post::get_controller_post_type(Post::class));
58 |
59 | // Child post
60 | $this->assertSame('page', Post::get_controller_post_type(Page::class));
61 |
62 | // TODO: Test classes with no explicit post type
63 | // TODO: Test template classes
64 | }
65 |
66 | public function test_wp_post_properties() {
67 | $post = $this->factory()->post->create_and_get();
68 | $controller = Post::get_controller($post);
69 |
70 | $properties = get_object_vars($post);
71 | foreach($properties as $key => $value) {
72 | $this->assertSame($value, $controller->$key, "Post controller should support the WP_Post->$key property");
73 | }
74 | }
75 |
76 | public function test_url() {
77 | $post = $this->factory()->post->create_and_get();
78 | $controller = Post::get_controller($post);
79 |
80 | $this->assertSame(get_permalink($post), $controller->url());
81 | }
82 |
83 | public function test_archive_url() {
84 | $this->assertEquals(get_post_type_archive_link('post'), Post::archive_url());
85 | }
86 |
87 | public function test_author() {
88 | $user = $this->factory()->user->create_and_get();
89 | $post = $this->factory()->post->create_and_get([
90 | 'post_author' => $user->ID
91 | ]);
92 |
93 | $post_controller = Post::get_controller($post);
94 | $user_controller = $post_controller->author();
95 |
96 | $this->assertEquals($user->ID, $user_controller->id);
97 | }
98 |
99 | public function test_date() {
100 | $post = $this->factory()->post->create_and_get();
101 | $controller = Post::get_controller($post);
102 |
103 | // Local timezone
104 | $timestamp = strtotime($post->post_date);
105 | $this->assertSame($timestamp, $controller->date('timestamp'));
106 | $this->assertSame(date('d:m:Y', $timestamp), $controller->date('d:m:Y'));
107 |
108 | // GMT
109 | $timestamp = strtotime($post->post_date_gmt);
110 | $this->assertSame($timestamp, $controller->date('timestamp', true));
111 | $this->assertSame(date('d:m:Y', $timestamp), $controller->date('d:m:Y', true));
112 | }
113 |
114 | public function test_modified() {
115 | $post = $this->factory()->post->create_and_get();
116 | $controller = Post::get_controller($post);
117 |
118 | // Local timezone
119 | $timestamp = strtotime($post->post_modified);
120 | $this->assertSame($timestamp, $controller->modified('timestamp'));
121 | $this->assertSame(date('d:m:Y', $timestamp), $controller->modified('d:m:Y'));
122 |
123 | // GMT
124 | $timestamp = strtotime($post->post_modified_gmt);
125 | $this->assertSame($timestamp, $controller->modified('timestamp', true));
126 | $this->assertSame(date('d:m:Y', $timestamp), $controller->modified('d:m:Y', true));
127 | }
128 |
129 | public function test_terms() {
130 | $term1 = $this->factory()->term->create_and_get();
131 | $term2 = $this->factory()->term->create_and_get();
132 | $term3 = $this->factory()->term->create_and_get();
133 |
134 | // Post with multiple terms
135 | $post = $this->factory()->post->create_and_get();
136 | wp_set_post_terms($post->ID, [ $term1->term_id, $term2->term_id ], 'post_tag');
137 |
138 | $controller = Post::get_controller($post);
139 | $terms = $controller->terms('post_tag');
140 |
141 | $this->assertSame(2, count($terms));
142 | $this->assertSame([$term1->term_id, $term2->term_id], wp_list_pluck($terms, 'id'));
143 |
144 | // Post wih no terms
145 | $post = $this->factory()->post->create_and_get();
146 | $controller = Post::get_controller($post);
147 |
148 | $this->assertEmpty($controller->terms('post_tag'));
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/tests/test-term-controller.php:
--------------------------------------------------------------------------------
1 | factory()->term->create_and_get();
8 | $controller = Term::get_controller($term);
9 |
10 | $this->assertSame(get_term_link($term), $controller->url());
11 | }
12 |
13 | public function test_parent() {
14 | $parent = $this->factory()->term->create_and_get();
15 | $child = $this->factory()->term->create_and_get([
16 | 'parent' => $parent->term_id
17 | ]);
18 |
19 | $controller = Term::get_controller($child);
20 | $parent_controller = $controller->parent();
21 |
22 | $this->assertSame($parent->term_id, $parent_controller->id);
23 | }
24 |
25 | public function test_children() {
26 | $parent = $this->factory()->term->create_and_get();
27 | $children = $this->factory()->term->create_many(2, [
28 | 'parent' => $parent->term_id
29 | ]);
30 |
31 | $controller = Term::get_controller($parent);
32 | $children = $controller->children();
33 |
34 | $this->assertSame($children, wp_list_pluck($controller->children(), 'term_id'));
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/vendor/autoload.php:
--------------------------------------------------------------------------------
1 |
7 | * Jordi Boggiano
8 | *
9 | * For the full copyright and license information, please view the LICENSE
10 | * file that was distributed with this source code.
11 | */
12 |
13 | namespace Composer\Autoload;
14 |
15 | /**
16 | * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
17 | *
18 | * $loader = new \Composer\Autoload\ClassLoader();
19 | *
20 | * // register classes with namespaces
21 | * $loader->add('Symfony\Component', __DIR__.'/component');
22 | * $loader->add('Symfony', __DIR__.'/framework');
23 | *
24 | * // activate the autoloader
25 | * $loader->register();
26 | *
27 | * // to enable searching the include path (eg. for PEAR packages)
28 | * $loader->setUseIncludePath(true);
29 | *
30 | * In this example, if you try to use a class in the Symfony\Component
31 | * namespace or one of its children (Symfony\Component\Console for instance),
32 | * the autoloader will first look for the class under the component/
33 | * directory, and it will then fallback to the framework/ directory if not
34 | * found before giving up.
35 | *
36 | * This class is loosely based on the Symfony UniversalClassLoader.
37 | *
38 | * @author Fabien Potencier
39 | * @author Jordi Boggiano
40 | * @see http://www.php-fig.org/psr/psr-0/
41 | * @see http://www.php-fig.org/psr/psr-4/
42 | */
43 | class ClassLoader
44 | {
45 | // PSR-4
46 | private $prefixLengthsPsr4 = array();
47 | private $prefixDirsPsr4 = array();
48 | private $fallbackDirsPsr4 = array();
49 |
50 | // PSR-0
51 | private $prefixesPsr0 = array();
52 | private $fallbackDirsPsr0 = array();
53 |
54 | private $useIncludePath = false;
55 | private $classMap = array();
56 | private $classMapAuthoritative = false;
57 | private $missingClasses = array();
58 | private $apcuPrefix;
59 |
60 | public function getPrefixes()
61 | {
62 | if (!empty($this->prefixesPsr0)) {
63 | return call_user_func_array('array_merge', $this->prefixesPsr0);
64 | }
65 |
66 | return array();
67 | }
68 |
69 | public function getPrefixesPsr4()
70 | {
71 | return $this->prefixDirsPsr4;
72 | }
73 |
74 | public function getFallbackDirs()
75 | {
76 | return $this->fallbackDirsPsr0;
77 | }
78 |
79 | public function getFallbackDirsPsr4()
80 | {
81 | return $this->fallbackDirsPsr4;
82 | }
83 |
84 | public function getClassMap()
85 | {
86 | return $this->classMap;
87 | }
88 |
89 | /**
90 | * @param array $classMap Class to filename map
91 | */
92 | public function addClassMap(array $classMap)
93 | {
94 | if ($this->classMap) {
95 | $this->classMap = array_merge($this->classMap, $classMap);
96 | } else {
97 | $this->classMap = $classMap;
98 | }
99 | }
100 |
101 | /**
102 | * Registers a set of PSR-0 directories for a given prefix, either
103 | * appending or prepending to the ones previously set for this prefix.
104 | *
105 | * @param string $prefix The prefix
106 | * @param array|string $paths The PSR-0 root directories
107 | * @param bool $prepend Whether to prepend the directories
108 | */
109 | public function add($prefix, $paths, $prepend = false)
110 | {
111 | if (!$prefix) {
112 | if ($prepend) {
113 | $this->fallbackDirsPsr0 = array_merge(
114 | (array) $paths,
115 | $this->fallbackDirsPsr0
116 | );
117 | } else {
118 | $this->fallbackDirsPsr0 = array_merge(
119 | $this->fallbackDirsPsr0,
120 | (array) $paths
121 | );
122 | }
123 |
124 | return;
125 | }
126 |
127 | $first = $prefix[0];
128 | if (!isset($this->prefixesPsr0[$first][$prefix])) {
129 | $this->prefixesPsr0[$first][$prefix] = (array) $paths;
130 |
131 | return;
132 | }
133 | if ($prepend) {
134 | $this->prefixesPsr0[$first][$prefix] = array_merge(
135 | (array) $paths,
136 | $this->prefixesPsr0[$first][$prefix]
137 | );
138 | } else {
139 | $this->prefixesPsr0[$first][$prefix] = array_merge(
140 | $this->prefixesPsr0[$first][$prefix],
141 | (array) $paths
142 | );
143 | }
144 | }
145 |
146 | /**
147 | * Registers a set of PSR-4 directories for a given namespace, either
148 | * appending or prepending to the ones previously set for this namespace.
149 | *
150 | * @param string $prefix The prefix/namespace, with trailing '\\'
151 | * @param array|string $paths The PSR-4 base directories
152 | * @param bool $prepend Whether to prepend the directories
153 | *
154 | * @throws \InvalidArgumentException
155 | */
156 | public function addPsr4($prefix, $paths, $prepend = false)
157 | {
158 | if (!$prefix) {
159 | // Register directories for the root namespace.
160 | if ($prepend) {
161 | $this->fallbackDirsPsr4 = array_merge(
162 | (array) $paths,
163 | $this->fallbackDirsPsr4
164 | );
165 | } else {
166 | $this->fallbackDirsPsr4 = array_merge(
167 | $this->fallbackDirsPsr4,
168 | (array) $paths
169 | );
170 | }
171 | } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
172 | // Register directories for a new namespace.
173 | $length = strlen($prefix);
174 | if ('\\' !== $prefix[$length - 1]) {
175 | throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
176 | }
177 | $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
178 | $this->prefixDirsPsr4[$prefix] = (array) $paths;
179 | } elseif ($prepend) {
180 | // Prepend directories for an already registered namespace.
181 | $this->prefixDirsPsr4[$prefix] = array_merge(
182 | (array) $paths,
183 | $this->prefixDirsPsr4[$prefix]
184 | );
185 | } else {
186 | // Append directories for an already registered namespace.
187 | $this->prefixDirsPsr4[$prefix] = array_merge(
188 | $this->prefixDirsPsr4[$prefix],
189 | (array) $paths
190 | );
191 | }
192 | }
193 |
194 | /**
195 | * Registers a set of PSR-0 directories for a given prefix,
196 | * replacing any others previously set for this prefix.
197 | *
198 | * @param string $prefix The prefix
199 | * @param array|string $paths The PSR-0 base directories
200 | */
201 | public function set($prefix, $paths)
202 | {
203 | if (!$prefix) {
204 | $this->fallbackDirsPsr0 = (array) $paths;
205 | } else {
206 | $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
207 | }
208 | }
209 |
210 | /**
211 | * Registers a set of PSR-4 directories for a given namespace,
212 | * replacing any others previously set for this namespace.
213 | *
214 | * @param string $prefix The prefix/namespace, with trailing '\\'
215 | * @param array|string $paths The PSR-4 base directories
216 | *
217 | * @throws \InvalidArgumentException
218 | */
219 | public function setPsr4($prefix, $paths)
220 | {
221 | if (!$prefix) {
222 | $this->fallbackDirsPsr4 = (array) $paths;
223 | } else {
224 | $length = strlen($prefix);
225 | if ('\\' !== $prefix[$length - 1]) {
226 | throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
227 | }
228 | $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
229 | $this->prefixDirsPsr4[$prefix] = (array) $paths;
230 | }
231 | }
232 |
233 | /**
234 | * Turns on searching the include path for class files.
235 | *
236 | * @param bool $useIncludePath
237 | */
238 | public function setUseIncludePath($useIncludePath)
239 | {
240 | $this->useIncludePath = $useIncludePath;
241 | }
242 |
243 | /**
244 | * Can be used to check if the autoloader uses the include path to check
245 | * for classes.
246 | *
247 | * @return bool
248 | */
249 | public function getUseIncludePath()
250 | {
251 | return $this->useIncludePath;
252 | }
253 |
254 | /**
255 | * Turns off searching the prefix and fallback directories for classes
256 | * that have not been registered with the class map.
257 | *
258 | * @param bool $classMapAuthoritative
259 | */
260 | public function setClassMapAuthoritative($classMapAuthoritative)
261 | {
262 | $this->classMapAuthoritative = $classMapAuthoritative;
263 | }
264 |
265 | /**
266 | * Should class lookup fail if not found in the current class map?
267 | *
268 | * @return bool
269 | */
270 | public function isClassMapAuthoritative()
271 | {
272 | return $this->classMapAuthoritative;
273 | }
274 |
275 | /**
276 | * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
277 | *
278 | * @param string|null $apcuPrefix
279 | */
280 | public function setApcuPrefix($apcuPrefix)
281 | {
282 | $this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null;
283 | }
284 |
285 | /**
286 | * The APCu prefix in use, or null if APCu caching is not enabled.
287 | *
288 | * @return string|null
289 | */
290 | public function getApcuPrefix()
291 | {
292 | return $this->apcuPrefix;
293 | }
294 |
295 | /**
296 | * Registers this instance as an autoloader.
297 | *
298 | * @param bool $prepend Whether to prepend the autoloader or not
299 | */
300 | public function register($prepend = false)
301 | {
302 | spl_autoload_register(array($this, 'loadClass'), true, $prepend);
303 | }
304 |
305 | /**
306 | * Unregisters this instance as an autoloader.
307 | */
308 | public function unregister()
309 | {
310 | spl_autoload_unregister(array($this, 'loadClass'));
311 | }
312 |
313 | /**
314 | * Loads the given class or interface.
315 | *
316 | * @param string $class The name of the class
317 | * @return bool|null True if loaded, null otherwise
318 | */
319 | public function loadClass($class)
320 | {
321 | if ($file = $this->findFile($class)) {
322 | includeFile($file);
323 |
324 | return true;
325 | }
326 | }
327 |
328 | /**
329 | * Finds the path to the file where the class is defined.
330 | *
331 | * @param string $class The name of the class
332 | *
333 | * @return string|false The path if found, false otherwise
334 | */
335 | public function findFile($class)
336 | {
337 | // class map lookup
338 | if (isset($this->classMap[$class])) {
339 | return $this->classMap[$class];
340 | }
341 | if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
342 | return false;
343 | }
344 | if (null !== $this->apcuPrefix) {
345 | $file = apcu_fetch($this->apcuPrefix.$class, $hit);
346 | if ($hit) {
347 | return $file;
348 | }
349 | }
350 |
351 | $file = $this->findFileWithExtension($class, '.php');
352 |
353 | // Search for Hack files if we are running on HHVM
354 | if (false === $file && defined('HHVM_VERSION')) {
355 | $file = $this->findFileWithExtension($class, '.hh');
356 | }
357 |
358 | if (null !== $this->apcuPrefix) {
359 | apcu_add($this->apcuPrefix.$class, $file);
360 | }
361 |
362 | if (false === $file) {
363 | // Remember that this class does not exist.
364 | $this->missingClasses[$class] = true;
365 | }
366 |
367 | return $file;
368 | }
369 |
370 | private function findFileWithExtension($class, $ext)
371 | {
372 | // PSR-4 lookup
373 | $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
374 |
375 | $first = $class[0];
376 | if (isset($this->prefixLengthsPsr4[$first])) {
377 | $subPath = $class;
378 | while (false !== $lastPos = strrpos($subPath, '\\')) {
379 | $subPath = substr($subPath, 0, $lastPos);
380 | $search = $subPath.'\\';
381 | if (isset($this->prefixDirsPsr4[$search])) {
382 | foreach ($this->prefixDirsPsr4[$search] as $dir) {
383 | $length = $this->prefixLengthsPsr4[$first][$search];
384 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
385 | return $file;
386 | }
387 | }
388 | }
389 | }
390 | }
391 |
392 | // PSR-4 fallback dirs
393 | foreach ($this->fallbackDirsPsr4 as $dir) {
394 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
395 | return $file;
396 | }
397 | }
398 |
399 | // PSR-0 lookup
400 | if (false !== $pos = strrpos($class, '\\')) {
401 | // namespaced class name
402 | $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
403 | . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
404 | } else {
405 | // PEAR-like class name
406 | $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
407 | }
408 |
409 | if (isset($this->prefixesPsr0[$first])) {
410 | foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
411 | if (0 === strpos($class, $prefix)) {
412 | foreach ($dirs as $dir) {
413 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
414 | return $file;
415 | }
416 | }
417 | }
418 | }
419 | }
420 |
421 | // PSR-0 fallback dirs
422 | foreach ($this->fallbackDirsPsr0 as $dir) {
423 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
424 | return $file;
425 | }
426 | }
427 |
428 | // PSR-0 include paths.
429 | if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
430 | return $file;
431 | }
432 |
433 | return false;
434 | }
435 | }
436 |
437 | /**
438 | * Scope isolated include.
439 | *
440 | * Prevents access to $this/self from included files.
441 | */
442 | function includeFile($file)
443 | {
444 | include $file;
445 | }
446 |
--------------------------------------------------------------------------------
/vendor/composer/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Copyright (c) Nils Adermann, Jordi Boggiano
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy
5 | of this software and associated documentation files (the "Software"), to deal
6 | in the Software without restriction, including without limitation the rights
7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | copies of the Software, and to permit persons to whom the Software is furnished
9 | to do so, subject to the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be included in all
12 | copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | THE SOFTWARE.
21 |
22 |
--------------------------------------------------------------------------------
/vendor/composer/autoload_classmap.php:
--------------------------------------------------------------------------------
1 | array($baseDir . '/src'),
10 | );
11 |
--------------------------------------------------------------------------------
/vendor/composer/autoload_real.php:
--------------------------------------------------------------------------------
1 | = 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
27 | if ($useStaticLoader) {
28 | require_once __DIR__ . '/autoload_static.php';
29 |
30 | call_user_func(\Composer\Autoload\ComposerStaticInit1d6a803d906d480e35087e781c0c01c8::getInitializer($loader));
31 | } else {
32 | $map = require __DIR__ . '/autoload_namespaces.php';
33 | foreach ($map as $namespace => $path) {
34 | $loader->set($namespace, $path);
35 | }
36 |
37 | $map = require __DIR__ . '/autoload_psr4.php';
38 | foreach ($map as $namespace => $path) {
39 | $loader->setPsr4($namespace, $path);
40 | }
41 |
42 | $classMap = require __DIR__ . '/autoload_classmap.php';
43 | if ($classMap) {
44 | $loader->addClassMap($classMap);
45 | }
46 | }
47 |
48 | $loader->register(true);
49 |
50 | return $loader;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/vendor/composer/autoload_static.php:
--------------------------------------------------------------------------------
1 |
11 | array (
12 | 'WPControllers\\' => 14,
13 | ),
14 | );
15 |
16 | public static $prefixDirsPsr4 = array (
17 | 'WPControllers\\' =>
18 | array (
19 | 0 => __DIR__ . '/../..' . '/src',
20 | ),
21 | );
22 |
23 | public static function getInitializer(ClassLoader $loader)
24 | {
25 | return \Closure::bind(function () use ($loader) {
26 | $loader->prefixLengthsPsr4 = ComposerStaticInit1d6a803d906d480e35087e781c0c01c8::$prefixLengthsPsr4;
27 | $loader->prefixDirsPsr4 = ComposerStaticInit1d6a803d906d480e35087e781c0c01c8::$prefixDirsPsr4;
28 |
29 | }, null, ClassLoader::class);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/wp-controllers.php:
--------------------------------------------------------------------------------
1 | register();
24 |
--------------------------------------------------------------------------------