Yoast SEO - Version 14.0.2

Version Description

Release Date: April 29th, 2020

Because weve changed the underlying framework of our Indexables technology, weve chosen to rebuild the table. This means you will have to go through the indexing process again. Were sorry. For sites with more than 10,000 posts, we advise using the WP CLI command to do the indexation on the server.

Bugfixes:

  • Fixes a bug where a fatal error would be thrown when a title contained more than 191 characters.
  • Fixes a bug where a fatal error would be thrown when a focus keyphrase contained more than 191 characters.
  • Fixes a bug where a fatal error would be thrown when search engines were disallowed from indexing the site.
  • Fixes a bug where a fatal error would be thrown on WooCommerce installations when the wpseo_metadesc filter was called with only 1 argument.
  • Fixes a bug where a fatal error would be thrown when using the WPSEO_Frontend class to get the meta description.
  • Fixes a bug where a fatal error would be thrown when WPSEO_Frontend or WPSEO_Breadcrumbs was called before the init action.
  • Fixes a bug where a non-object property retrieval notice would be thrown when the site's content was being indexed.
  • Fixes a bug where a trailing slash would be added to canonical URLs and some rel="prev" URLs, even when the permalink structure settings didn't contain that trailing slash.
  • Fixes a bug where a double breadcrumb would be shown on home pages.
  • Fixes a bug where the indexation would continue indefinitely under specific circumstances.

Other:

  • Removes all usages of PDO and mysqli directly and uses wpdb everywhere. This should prevent a lot of errors for database installations that have different encodings or configurations than what is generally seen.
Download this release

Release Info

Developer Yoast
Plugin Icon 128x128 Yoast SEO
Version 14.0.2
Comparing to
See all releases

Code changes from version 14.0.1 to 14.0.2

Files changed (128) hide show
  1. config/php-scoper/idiorm.inc.php +0 -37
  2. css/dist/{admin-global-1401-rtl.css → admin-global-1402-rtl.css} +0 -0
  3. css/dist/{admin-global-1401.css → admin-global-1402.css} +0 -0
  4. css/dist/{adminbar-1401-rtl.css → adminbar-1402-rtl.css} +0 -0
  5. css/dist/{adminbar-1401.css → adminbar-1402.css} +0 -0
  6. css/dist/{alerts-1401-rtl.css → alerts-1402-rtl.css} +0 -0
  7. css/dist/{alerts-1401.css → alerts-1402.css} +0 -0
  8. css/dist/{dashboard-1401-rtl.css → dashboard-1402-rtl.css} +0 -0
  9. css/dist/{dashboard-1401.css → dashboard-1402.css} +0 -0
  10. css/dist/{edit-page-1401-rtl.css → edit-page-1402-rtl.css} +0 -0
  11. css/dist/{edit-page-1401.css → edit-page-1402.css} +0 -0
  12. css/dist/{featured-image-1401-rtl.css → featured-image-1402-rtl.css} +0 -0
  13. css/dist/{featured-image-1401.css → featured-image-1402.css} +0 -0
  14. css/dist/{filter-explanation-1401-rtl.css → filter-explanation-1402-rtl.css} +0 -0
  15. css/dist/{filter-explanation-1401.css → filter-explanation-1402.css} +0 -0
  16. css/dist/{inside-editor-1401-rtl.css → inside-editor-1402-rtl.css} +0 -0
  17. css/dist/{inside-editor-1401.css → inside-editor-1402.css} +0 -0
  18. css/dist/{metabox-1401-rtl.css → metabox-1402-rtl.css} +0 -0
  19. css/dist/{metabox-1401.css → metabox-1402.css} +0 -0
  20. css/dist/{metabox-primary-category-1401-rtl.css → metabox-primary-category-1402-rtl.css} +0 -0
  21. css/dist/{metabox-primary-category-1401.css → metabox-primary-category-1402.css} +0 -0
  22. css/dist/{monorepo-1401-rtl.css → monorepo-1402-rtl.css} +0 -0
  23. css/dist/{monorepo-1401.css → monorepo-1402.css} +0 -0
  24. css/dist/{search-appearance-1401-rtl.css → search-appearance-1402-rtl.css} +0 -0
  25. css/dist/{search-appearance-1401.css → search-appearance-1402.css} +0 -0
  26. css/dist/{structured-data-blocks-1401-rtl.css → structured-data-blocks-1402-rtl.css} +0 -0
  27. css/dist/{structured-data-blocks-1401.css → structured-data-blocks-1402.css} +0 -0
  28. css/dist/{toggle-switch-1401-rtl.css → toggle-switch-1402-rtl.css} +0 -0
  29. css/dist/{toggle-switch-1401.css → toggle-switch-1402.css} +0 -0
  30. css/dist/{wpseo-dismissible-1401-rtl.css → wpseo-dismissible-1402-rtl.css} +0 -0
  31. css/dist/{wpseo-dismissible-1401.css → wpseo-dismissible-1402.css} +0 -0
  32. css/dist/{yoast-components-1401-rtl.css → yoast-components-1402-rtl.css} +0 -0
  33. css/dist/{yoast-components-1401.css → yoast-components-1402.css} +0 -0
  34. css/dist/{yoast-extensions-1401-rtl.css → yoast-extensions-1402-rtl.css} +0 -0
  35. css/dist/{yoast-extensions-1401.css → yoast-extensions-1402.css} +0 -0
  36. css/dist/{yst_plugin_tools-1401-rtl.css → yst_plugin_tools-1402-rtl.css} +0 -0
  37. css/dist/{yst_plugin_tools-1401.css → yst_plugin_tools-1402.css} +0 -0
  38. css/dist/{yst_seo_score-1401-rtl.css → yst_seo_score-1402-rtl.css} +0 -0
  39. css/dist/{yst_seo_score-1401.css → yst_seo_score-1402.css} +0 -0
  40. {src/backwards-compatibility → deprecated/frontend}/breadcrumbs.php +11 -26
  41. {src/backwards-compatibility → deprecated/frontend}/frontend.php +28 -26
  42. js/dist/{analysis-1401.js → analysis-1402.js} +0 -0
  43. js/dist/{babel-polyfill-1401.js → babel-polyfill-1402.js} +0 -0
  44. js/dist/{commons-1401.js → commons-1402.js} +0 -0
  45. js/dist/{components-1401.js → components-1402.js} +0 -0
  46. js/dist/{configuration-wizard-1401.js → configuration-wizard-1402.js} +0 -0
  47. js/dist/{help-scout-beacon-1401.js → help-scout-beacon-1402.js} +0 -0
  48. js/dist/{jed-1401.js → jed-1402.js} +0 -0
  49. js/dist/{redux-1401.js → redux-1402.js} +0 -0
  50. js/dist/{search-appearance-1401.js → search-appearance-1402.js} +0 -0
  51. js/dist/{styled-components-1401.js → styled-components-1402.js} +0 -0
  52. js/dist/{wp-seo-admin-1401.js → wp-seo-admin-1402.js} +0 -0
  53. js/dist/{wp-seo-admin-global-1401.js → wp-seo-admin-global-1402.js} +0 -0
  54. js/dist/{wp-seo-admin-gsc-1401.js → wp-seo-admin-gsc-1402.js} +0 -0
  55. js/dist/{wp-seo-admin-media-1401.js → wp-seo-admin-media-1402.js} +0 -0
  56. js/dist/{wp-seo-analysis-worker-1401.js → wp-seo-analysis-worker-1402.js} +0 -0
  57. js/dist/{wp-seo-api-1401.js → wp-seo-api-1402.js} +0 -0
  58. js/dist/{wp-seo-bulk-editor-1401.js → wp-seo-bulk-editor-1402.js} +0 -0
  59. js/dist/{wp-seo-dashboard-widget-1401.js → wp-seo-dashboard-widget-1402.js} +0 -0
  60. js/dist/{wp-seo-edit-page-1401.js → wp-seo-edit-page-1402.js} +0 -0
  61. js/dist/{wp-seo-featured-image-1401.js → wp-seo-featured-image-1402.js} +0 -0
  62. js/dist/{wp-seo-filter-explanation-1401.js → wp-seo-filter-explanation-1402.js} +0 -0
  63. js/dist/{wp-seo-indexation-1401.js → wp-seo-indexation-1402.js} +0 -0
  64. js/dist/{wp-seo-metabox-1401.js → wp-seo-metabox-1402.js} +0 -0
  65. js/dist/{wp-seo-metabox-category-1401.js → wp-seo-metabox-category-1402.js} +0 -0
  66. js/dist/{wp-seo-modal-1401.js → wp-seo-modal-1402.js} +0 -0
  67. js/dist/{wp-seo-network-admin-1401.js → wp-seo-network-admin-1402.js} +0 -0
  68. js/dist/{wp-seo-post-scraper-1401.js → wp-seo-post-scraper-1402.js} +0 -0
  69. js/dist/{wp-seo-quick-edit-handler-1401.js → wp-seo-quick-edit-handler-1402.js} +0 -0
  70. js/dist/{wp-seo-recalculate-1401.js → wp-seo-recalculate-1402.js} +0 -0
  71. js/dist/{wp-seo-reindex-links-1401.js → wp-seo-reindex-links-1402.js} +0 -0
  72. js/dist/{wp-seo-replacevar-plugin-1401.js → wp-seo-replacevar-plugin-1402.js} +0 -0
  73. js/dist/{wp-seo-shortcode-plugin-1401.js → wp-seo-shortcode-plugin-1402.js} +0 -0
  74. js/dist/{wp-seo-structured-data-blocks-1401.js → wp-seo-structured-data-blocks-1402.js} +0 -0
  75. js/dist/{wp-seo-term-scraper-1401.js → wp-seo-term-scraper-1402.js} +0 -0
  76. js/dist/{wp-seo-used-keywords-assessment-1401.js → wp-seo-used-keywords-assessment-1402.js} +0 -0
  77. languages/wordpress-seojs-de_DE.json +1 -1
  78. languages/yoast-components-de_DE.json +1 -1
  79. src/orm/yoast-model.php → lib/model.php +28 -80
  80. lib/orm.php +2342 -0
  81. lib/ruckusing-adapter.php +1084 -0
  82. lib/ruckusing-framework-runner.php +266 -0
  83. migrations/20171228151840_WpYoastIndexable.php +2 -2
  84. migrations/20171228151841_WpYoastPrimaryTerm.php +2 -2
  85. migrations/20190529075038_WpYoastDropIndexableMetaTableIfExists.php +2 -2
  86. migrations/20191011111109_WpYoastIndexableHierarchy.php +2 -2
  87. migrations/20200408101900_AddCollationToTables.php +5 -5
  88. migrations/20200420073606_AddColumnsToIndexables.php +4 -4
  89. migrations/20200428123747_BreadcrumbTitleAndHierarchyReset.php +3 -3
  90. migrations/20200428194858_ExpandIndexableColumnLengths.php +71 -0
  91. migrations/20200429105310_TruncateIndexableTables.php +48 -0
  92. polyfills/pdo/pdo-mysqli-polyfill.php +0 -297
  93. polyfills/pdo/pdo-mysqli-statement-polyfill.php +0 -448
  94. readme.txt +23 -1
  95. src/actions/indexation/indexable-post-indexation-action.php +3 -6
  96. src/actions/indexation/indexable-term-indexation-action.php +3 -6
  97. src/builders/indexable-builder.php +11 -3
  98. src/builders/indexable-hierarchy-builder.php +3 -3
  99. src/config/ruckusing-framework.php +4 -41
  100. src/generated/container.php +6 -33
  101. src/generators/breadcrumbs-generator.php +3 -3
  102. src/helpers/author-archive-helper.php +3 -3
  103. src/helpers/post-helper.php +4 -4
  104. src/helpers/robots-helper.php +13 -6
  105. src/initializers/database-setup.php +0 -115
  106. src/initializers/migration-runner.php +4 -42
  107. src/integrations/third-party/woocommerce.php +41 -7
  108. src/integrations/watchers/indexable-permalink-watcher.php +2 -2
  109. src/integrations/watchers/indexable-post-watcher.php +0 -4
  110. src/integrations/watchers/option-titles-watcher.php +5 -5
  111. src/loggers/database-logger.php +0 -173
  112. src/models/indexable-extension.php +2 -2
  113. src/models/indexable-hierarchy.php +2 -2
  114. src/models/indexable.php +9 -3
  115. src/models/primary-term.php +2 -2
  116. src/models/seo-links.php +2 -2
  117. src/models/seo-meta.php +2 -2
  118. src/orm/yoast-orm-wrapper.php +0 -234
  119. src/presenters/admin/indexation-warning-presenter.php +2 -1
  120. src/repositories/indexable-hierarchy-repository.php +3 -3
  121. src/repositories/indexable-repository.php +4 -11
  122. src/repositories/primary-term-repository.php +2 -2
  123. src/repositories/seo-links-repository.php +2 -2
  124. src/repositories/seo-meta-repository.php +2 -2
  125. vendor/composer/autoload_classmap.php +6 -6
  126. vendor/composer/autoload_static.php +6 -6
  127. wp-seo-main.php +1 -6
  128. wp-seo.php +1 -1
config/php-scoper/idiorm.inc.php DELETED
@@ -1,37 +0,0 @@
1
- <?php
2
-
3
- declare(strict_types = 1);
4
-
5
- use Isolated\Symfony\Component\Finder\Finder;
6
-
7
- return array(
8
-
9
- /*
10
- * By default when running php-scoper add-prefix, it will prefix all relevant code found in the current working
11
- * directory. You can however define which files should be scoped by defining a collection of Finders in the
12
- * following configuration key.
13
- *
14
- * For more see: https://github.com/humbug/php-scoper#finders-and-paths
15
- */
16
- 'finders' => array(
17
- Finder::create()->files()->in( 'vendor/j4mie/idiorm' )->name( [ 'idiorm.php', 'LICENSE', 'composer.json' ] ),
18
- ),
19
-
20
- /*
21
- * When scoping PHP files, there will be scenarios where some of the code being scoped indirectly references the
22
- * original namespace. These will include, for example, strings or string manipulations. PHP-Scoper has limited
23
- * support for prefixing such strings. To circumvent that, you can define patchers to manipulate the file to your
24
- * heart contents.
25
- *
26
- * For more see: https://github.com/humbug/php-scoper#patchers
27
- */
28
- 'patchers' => array(),
29
-
30
- /*
31
- * By default, PHP-Scoper will not prefix the user defined constants, classes and functions belonging to the global
32
- * namespace. You can however change that setting for them to be prefixed as usual unless explicitly whitelisted.
33
- *
34
- * https://github.com/humbug/php-scoper#whitelist
35
- */
36
- 'whitelist-global-classes' => false,
37
- );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
css/dist/{admin-global-1401-rtl.css → admin-global-1402-rtl.css} RENAMED
File without changes
css/dist/{admin-global-1401.css → admin-global-1402.css} RENAMED
File without changes
css/dist/{adminbar-1401-rtl.css → adminbar-1402-rtl.css} RENAMED
File without changes
css/dist/{adminbar-1401.css → adminbar-1402.css} RENAMED
File without changes
css/dist/{alerts-1401-rtl.css → alerts-1402-rtl.css} RENAMED
File without changes
css/dist/{alerts-1401.css → alerts-1402.css} RENAMED
File without changes
css/dist/{dashboard-1401-rtl.css → dashboard-1402-rtl.css} RENAMED
File without changes
css/dist/{dashboard-1401.css → dashboard-1402.css} RENAMED
File without changes
css/dist/{edit-page-1401-rtl.css → edit-page-1402-rtl.css} RENAMED
File without changes
css/dist/{edit-page-1401.css → edit-page-1402.css} RENAMED
File without changes
css/dist/{featured-image-1401-rtl.css → featured-image-1402-rtl.css} RENAMED
File without changes
css/dist/{featured-image-1401.css → featured-image-1402.css} RENAMED
File without changes
css/dist/{filter-explanation-1401-rtl.css → filter-explanation-1402-rtl.css} RENAMED
File without changes
css/dist/{filter-explanation-1401.css → filter-explanation-1402.css} RENAMED
File without changes
css/dist/{inside-editor-1401-rtl.css → inside-editor-1402-rtl.css} RENAMED
File without changes
css/dist/{inside-editor-1401.css → inside-editor-1402.css} RENAMED
File without changes
css/dist/{metabox-1401-rtl.css → metabox-1402-rtl.css} RENAMED
File without changes
css/dist/{metabox-1401.css → metabox-1402.css} RENAMED
File without changes
css/dist/{metabox-primary-category-1401-rtl.css → metabox-primary-category-1402-rtl.css} RENAMED
File without changes
css/dist/{metabox-primary-category-1401.css → metabox-primary-category-1402.css} RENAMED
File without changes
css/dist/{monorepo-1401-rtl.css → monorepo-1402-rtl.css} RENAMED
File without changes
css/dist/{monorepo-1401.css → monorepo-1402.css} RENAMED
File without changes
css/dist/{search-appearance-1401-rtl.css → search-appearance-1402-rtl.css} RENAMED
File without changes
css/dist/{search-appearance-1401.css → search-appearance-1402.css} RENAMED
File without changes
css/dist/{structured-data-blocks-1401-rtl.css → structured-data-blocks-1402-rtl.css} RENAMED
File without changes
css/dist/{structured-data-blocks-1401.css → structured-data-blocks-1402.css} RENAMED
File without changes
css/dist/{toggle-switch-1401-rtl.css → toggle-switch-1402-rtl.css} RENAMED
File without changes
css/dist/{toggle-switch-1401.css → toggle-switch-1402.css} RENAMED
File without changes
css/dist/{wpseo-dismissible-1401-rtl.css → wpseo-dismissible-1402-rtl.css} RENAMED
File without changes
css/dist/{wpseo-dismissible-1401.css → wpseo-dismissible-1402.css} RENAMED
File without changes
css/dist/{yoast-components-1401-rtl.css → yoast-components-1402-rtl.css} RENAMED
File without changes
css/dist/{yoast-components-1401.css → yoast-components-1402.css} RENAMED
File without changes
css/dist/{yoast-extensions-1401-rtl.css → yoast-extensions-1402-rtl.css} RENAMED
File without changes
css/dist/{yoast-extensions-1401.css → yoast-extensions-1402.css} RENAMED
File without changes
css/dist/{yst_plugin_tools-1401-rtl.css → yst_plugin_tools-1402-rtl.css} RENAMED
File without changes
css/dist/{yst_plugin_tools-1401.css → yst_plugin_tools-1402.css} RENAMED
File without changes
css/dist/{yst_seo_score-1401-rtl.css → yst_seo_score-1402-rtl.css} RENAMED
File without changes
css/dist/{yst_seo_score-1401.css → yst_seo_score-1402.css} RENAMED
File without changes
{src/backwards-compatibility → deprecated/frontend}/breadcrumbs.php RENAMED
@@ -5,8 +5,6 @@
5
  * @package Yoast\YoastSEO\Backwards_Compatibility
6
  */
7
 
8
- use Yoast\WP\SEO\Conditionals\No_Conditionals;
9
- use Yoast\WP\SEO\Initializers\Initializer_Interface;
10
  use Yoast\WP\SEO\Memoizers\Meta_Tags_Context_Memoizer;
11
  use Yoast\WP\SEO\Presenters\Breadcrumbs_Presenter;
12
  use Yoast\WP\SEO\Surfaces\Helpers_Surface;
@@ -16,9 +14,7 @@ use Yoast\WP\SEO\Surfaces\Helpers_Surface;
16
  *
17
  * @codeCoverageIgnore Because of deprecation.
18
  */
19
- class WPSEO_Breadcrumbs implements Initializer_Interface {
20
-
21
- use No_Conditionals;
22
 
23
  /**
24
  * Instance of this class.
@@ -64,26 +60,11 @@ class WPSEO_Breadcrumbs implements Initializer_Interface {
64
 
65
  /**
66
  * WPSEO_Breadcrumbs constructor.
67
- *
68
- * @param Meta_Tags_Context_Memoizer $context_memoizer The context memoizer.
69
- * @param Helpers_Surface $helpers The helpers surface.
70
- * @param WPSEO_Replace_Vars $replace_vars The replace vars helper.
71
- */
72
- public function __construct(
73
- Meta_Tags_Context_Memoizer $context_memoizer,
74
- Helpers_Surface $helpers,
75
- WPSEO_Replace_Vars $replace_vars
76
- ) {
77
- $this->context_memoizer = $context_memoizer;
78
- $this->helpers = $helpers;
79
- $this->replace_vars = $replace_vars;
80
- }
81
-
82
- /**
83
- * We use an initializer so the static functions will work right as our plugin is loaded just as they normally would.
84
  */
85
- public function initialize() {
86
- self::$instance = $this;
 
 
87
  }
88
 
89
  /**
@@ -99,7 +80,7 @@ class WPSEO_Breadcrumbs implements Initializer_Interface {
99
  // Remember the last used before/after for use in case the object goes __toString().
100
  self::$before = $before;
101
  self::$after = $after;
102
- $output = $before . self::$instance->render() . $after;
103
 
104
  if ( $display === true ) {
105
  echo $output;
@@ -122,9 +103,13 @@ class WPSEO_Breadcrumbs implements Initializer_Interface {
122
  /**
123
  * Retrieves an instance of the class.
124
  *
125
- * @return WPSEO_Breadcrumbs The instance.
126
  */
127
  public static function get_instance() {
 
 
 
 
128
  return self::$instance;
129
  }
130
 
5
  * @package Yoast\YoastSEO\Backwards_Compatibility
6
  */
7
 
 
 
8
  use Yoast\WP\SEO\Memoizers\Meta_Tags_Context_Memoizer;
9
  use Yoast\WP\SEO\Presenters\Breadcrumbs_Presenter;
10
  use Yoast\WP\SEO\Surfaces\Helpers_Surface;
14
  *
15
  * @codeCoverageIgnore Because of deprecation.
16
  */
17
+ class WPSEO_Breadcrumbs {
 
 
18
 
19
  /**
20
  * Instance of this class.
60
 
61
  /**
62
  * WPSEO_Breadcrumbs constructor.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
  */
64
+ public function __construct() {
65
+ $this->context_memoizer = YoastSEO()->classes->get( Meta_Tags_Context_Memoizer::class );
66
+ $this->helpers = YoastSEO()->classes->get( Helpers_Surface::class );
67
+ $this->replace_vars = YoastSEO()->classes->get( WPSEO_Replace_Vars::class );
68
  }
69
 
70
  /**
80
  // Remember the last used before/after for use in case the object goes __toString().
81
  self::$before = $before;
82
  self::$after = $after;
83
+ $output = $before . self::get_instance()->render() . $after;
84
 
85
  if ( $display === true ) {
86
  echo $output;
103
  /**
104
  * Retrieves an instance of the class.
105
  *
106
+ * @return static The instance.
107
  */
108
  public static function get_instance() {
109
+ if ( is_null( self::$instance ) ) {
110
+ self::$instance = new self();
111
+ }
112
+
113
  return self::$instance;
114
  }
115
 
{src/backwards-compatibility → deprecated/frontend}/frontend.php RENAMED
@@ -5,20 +5,20 @@
5
  * @package Yoast\YoastSEO\Backwards_Compatibility
6
  */
7
 
8
- use Yoast\WP\SEO\Initializers\Initializer_Interface;
9
  use Yoast\WP\SEO\Memoizers\Meta_Tags_Context_Memoizer;
10
  use Yoast\WP\SEO\Presenters\Canonical_Presenter;
11
  use Yoast\WP\SEO\Presenters\Meta_Description_Presenter;
12
  use Yoast\WP\SEO\Presenters\Rel_Next_Presenter;
13
  use Yoast\WP\SEO\Presenters\Rel_Prev_Presenter;
14
  use Yoast\WP\SEO\Presenters\Robots_Presenter;
 
15
 
16
  /**
17
  * Class WPSEO_Frontend
18
  *
19
  * @codeCoverageIgnore Because of deprecation.
20
  */
21
- class WPSEO_Frontend implements Initializer_Interface {
22
  /**
23
  * Instance of this class.
24
  *
@@ -41,31 +41,19 @@ class WPSEO_Frontend implements Initializer_Interface {
41
  private $replace_vars;
42
 
43
  /**
44
- * @inheritDoc
 
 
45
  */
46
- public function initialize() {
47
- self::$instance = $this;
48
- }
49
 
50
  /**
51
- * @inheritDoc
52
  */
53
- public static function get_conditionals() {
54
- return [];
55
- }
56
-
57
- /**
58
- * WPSEO_Breadcrumbs constructor.
59
- *
60
- * @param Meta_Tags_Context_Memoizer $context_memoizer The context memoizer.
61
- * @param \WPSEO_Replace_Vars $replace_vars The replace vars helper.
62
- */
63
- public function __construct(
64
- Meta_Tags_Context_Memoizer $context_memoizer,
65
- WPSEO_Replace_Vars $replace_vars
66
- ) {
67
- $this->context_memoizer = $context_memoizer;
68
- $this->replace_vars = $replace_vars;
69
  }
70
 
71
  /**
@@ -101,6 +89,10 @@ class WPSEO_Frontend implements Initializer_Interface {
101
  * @return static The instance.
102
  */
103
  public static function get_instance() {
 
 
 
 
104
  return self::$instance;
105
  }
106
 
@@ -121,9 +113,11 @@ class WPSEO_Frontend implements Initializer_Interface {
121
  return $context->presentation->canonical;
122
  }
123
 
124
- $canonical_presenter = new Canonical_Presenter();
125
- $canonical_presenter->presentation = $context->presentation;
126
- echo $canonical_presenter->present();
 
 
127
  }
128
 
129
  /**
@@ -148,6 +142,8 @@ class WPSEO_Frontend implements Initializer_Interface {
148
  $context = $this->context_memoizer->for_current_page();
149
  $presenter = new Robots_Presenter();
150
  $presenter->presentation = $context->presentation;
 
 
151
  echo $presenter->present();
152
  }
153
 
@@ -230,10 +226,14 @@ class WPSEO_Frontend implements Initializer_Interface {
230
 
231
  $rel_prev_presenter = new Rel_Prev_Presenter();
232
  $rel_prev_presenter->presentation = $context->presentation;
 
 
233
  echo $rel_prev_presenter->present();
234
 
235
  $rel_next_presenter = new Rel_Next_Presenter();
236
  $rel_next_presenter->presentation = $context->presentation;
 
 
237
  echo $rel_next_presenter->present();
238
  }
239
 
@@ -255,6 +255,8 @@ class WPSEO_Frontend implements Initializer_Interface {
255
 
256
  $presenter = new Meta_Description_Presenter();
257
  $presenter->presentation = $context->presentation;
 
 
258
  $presenter->present();
259
  }
260
  }
5
  * @package Yoast\YoastSEO\Backwards_Compatibility
6
  */
7
 
 
8
  use Yoast\WP\SEO\Memoizers\Meta_Tags_Context_Memoizer;
9
  use Yoast\WP\SEO\Presenters\Canonical_Presenter;
10
  use Yoast\WP\SEO\Presenters\Meta_Description_Presenter;
11
  use Yoast\WP\SEO\Presenters\Rel_Next_Presenter;
12
  use Yoast\WP\SEO\Presenters\Rel_Prev_Presenter;
13
  use Yoast\WP\SEO\Presenters\Robots_Presenter;
14
+ use Yoast\WP\SEO\Surfaces\Helpers_Surface;
15
 
16
  /**
17
  * Class WPSEO_Frontend
18
  *
19
  * @codeCoverageIgnore Because of deprecation.
20
  */
21
+ class WPSEO_Frontend {
22
  /**
23
  * Instance of this class.
24
  *
41
  private $replace_vars;
42
 
43
  /**
44
+ * The helpers surface.
45
+ *
46
+ * @var Helpers_Surface
47
  */
48
+ private $helpers;
 
 
49
 
50
  /**
51
+ * WPSEO_Frontend constructor.
52
  */
53
+ public function __construct() {
54
+ $this->context_memoizer = YoastSEO()->classes->get( Meta_Tags_Context_Memoizer::class );
55
+ $this->replace_vars = YoastSEO()->classes->get( WPSEO_Replace_Vars::class );
56
+ $this->helpers = YoastSEO()->classes->get( Helpers_Surface::class );
 
 
 
 
 
 
 
 
 
 
 
 
57
  }
58
 
59
  /**
89
  * @return static The instance.
90
  */
91
  public static function get_instance() {
92
+ if ( is_null( self::$instance ) ) {
93
+ self::$instance = new self();
94
+ }
95
+
96
  return self::$instance;
97
  }
98
 
113
  return $context->presentation->canonical;
114
  }
115
 
116
+ $presenter = new Canonical_Presenter();
117
+ $presenter->presentation = $context->presentation;
118
+ $presenter->helpers = $this->helpers;
119
+ $presenter->replace_vars = $this->replace_vars;
120
+ echo $presenter->present();
121
  }
122
 
123
  /**
142
  $context = $this->context_memoizer->for_current_page();
143
  $presenter = new Robots_Presenter();
144
  $presenter->presentation = $context->presentation;
145
+ $presenter->helpers = $this->helpers;
146
+ $presenter->replace_vars = $this->replace_vars;
147
  echo $presenter->present();
148
  }
149
 
226
 
227
  $rel_prev_presenter = new Rel_Prev_Presenter();
228
  $rel_prev_presenter->presentation = $context->presentation;
229
+ $rel_prev_presenter->helpers = $this->helpers;
230
+ $rel_prev_presenter->replace_vars = $this->replace_vars;
231
  echo $rel_prev_presenter->present();
232
 
233
  $rel_next_presenter = new Rel_Next_Presenter();
234
  $rel_next_presenter->presentation = $context->presentation;
235
+ $rel_next_presenter->helpers = $this->helpers;
236
+ $rel_next_presenter->replace_vars = $this->replace_vars;
237
  echo $rel_next_presenter->present();
238
  }
239
 
255
 
256
  $presenter = new Meta_Description_Presenter();
257
  $presenter->presentation = $context->presentation;
258
+ $presenter->helpers = $this->helpers;
259
+ $presenter->replace_vars = $this->replace_vars;
260
  $presenter->present();
261
  }
262
  }
js/dist/{analysis-1401.js → analysis-1402.js} RENAMED
File without changes
js/dist/{babel-polyfill-1401.js → babel-polyfill-1402.js} RENAMED
File without changes
js/dist/{commons-1401.js → commons-1402.js} RENAMED
File without changes
js/dist/{components-1401.js → components-1402.js} RENAMED
File without changes
js/dist/{configuration-wizard-1401.js → configuration-wizard-1402.js} RENAMED
File without changes
js/dist/{help-scout-beacon-1401.js → help-scout-beacon-1402.js} RENAMED
File without changes
js/dist/{jed-1401.js → jed-1402.js} RENAMED
File without changes
js/dist/{redux-1401.js → redux-1402.js} RENAMED
File without changes
js/dist/{search-appearance-1401.js → search-appearance-1402.js} RENAMED
File without changes
js/dist/{styled-components-1401.js → styled-components-1402.js} RENAMED
File without changes
js/dist/{wp-seo-admin-1401.js → wp-seo-admin-1402.js} RENAMED
File without changes
js/dist/{wp-seo-admin-global-1401.js → wp-seo-admin-global-1402.js} RENAMED
File without changes
js/dist/{wp-seo-admin-gsc-1401.js → wp-seo-admin-gsc-1402.js} RENAMED
File without changes
js/dist/{wp-seo-admin-media-1401.js → wp-seo-admin-media-1402.js} RENAMED
File without changes
js/dist/{wp-seo-analysis-worker-1401.js → wp-seo-analysis-worker-1402.js} RENAMED
File without changes
js/dist/{wp-seo-api-1401.js → wp-seo-api-1402.js} RENAMED
File without changes
js/dist/{wp-seo-bulk-editor-1401.js → wp-seo-bulk-editor-1402.js} RENAMED
File without changes
js/dist/{wp-seo-dashboard-widget-1401.js → wp-seo-dashboard-widget-1402.js} RENAMED
File without changes
js/dist/{wp-seo-edit-page-1401.js → wp-seo-edit-page-1402.js} RENAMED
File without changes
js/dist/{wp-seo-featured-image-1401.js → wp-seo-featured-image-1402.js} RENAMED
File without changes
js/dist/{wp-seo-filter-explanation-1401.js → wp-seo-filter-explanation-1402.js} RENAMED
File without changes
js/dist/{wp-seo-indexation-1401.js → wp-seo-indexation-1402.js} RENAMED
File without changes
js/dist/{wp-seo-metabox-1401.js → wp-seo-metabox-1402.js} RENAMED
File without changes
js/dist/{wp-seo-metabox-category-1401.js → wp-seo-metabox-category-1402.js} RENAMED
File without changes
js/dist/{wp-seo-modal-1401.js → wp-seo-modal-1402.js} RENAMED
File without changes
js/dist/{wp-seo-network-admin-1401.js → wp-seo-network-admin-1402.js} RENAMED
File without changes
js/dist/{wp-seo-post-scraper-1401.js → wp-seo-post-scraper-1402.js} RENAMED
File without changes
js/dist/{wp-seo-quick-edit-handler-1401.js → wp-seo-quick-edit-handler-1402.js} RENAMED
File without changes
js/dist/{wp-seo-recalculate-1401.js → wp-seo-recalculate-1402.js} RENAMED
File without changes
js/dist/{wp-seo-reindex-links-1401.js → wp-seo-reindex-links-1402.js} RENAMED
File without changes
js/dist/{wp-seo-replacevar-plugin-1401.js → wp-seo-replacevar-plugin-1402.js} RENAMED
File without changes
js/dist/{wp-seo-shortcode-plugin-1401.js → wp-seo-shortcode-plugin-1402.js} RENAMED
File without changes
js/dist/{wp-seo-structured-data-blocks-1401.js → wp-seo-structured-data-blocks-1402.js} RENAMED
File without changes
js/dist/{wp-seo-term-scraper-1401.js → wp-seo-term-scraper-1402.js} RENAMED
File without changes
js/dist/{wp-seo-used-keywords-assessment-1401.js → wp-seo-used-keywords-assessment-1402.js} RENAMED
File without changes
languages/wordpress-seojs-de_DE.json CHANGED
@@ -1 +1 @@
1
- {"domain":"wordpress-seo","locale_data":{"wordpress-seo":{"":{"domain":"wordpress-seo","plural-forms":"nplurals=2; plural=n != 1;","lang":"de"},"Google preview":["Google-Vorschau"],"When you click OK we will open our HelpScout beacon where you can find answers to your questions. This beacon will load our support data and also potentially set cookies.":[],"Schema":["Schema"],"Truly optimize your site for a local audience with our %s plugin! Optimized address details, opening hours, store locator and pickup option!":["Optimiere deine Website für ein lokales Publikum mit unserem %s-Plugin! Optimierte Adressdaten, Öffnungszeiten, Filialfinder und Abhol-Option!"],"Serving local customers?":["Betreuung lokaler Kunden?"],"Get the %s plugin now":["Hol dir jetzt das %s-Plugin"],"You can edit the details shown in meta data, like the social profiles, the name and the description of this user on their %1$s profile page.":["Du kannst die Angaben zu sozialen Profilen, den Namen und die Beschreibung dieses Benutzers, die in den Metadaten sichtbar sind, auf dessen %1$s Profilseite bearbeiten."],"Select a user...":["Wähle einen Benutzer…"],"Name:":["Name:"],"You have selected the user %1$s as the person this site represents. Their user profile information will now be used in search results. %2$sUpdate their profile to make sure the information is correct.%3$s":["Du hast den Benutzer %1$s als die Person ausgewählt, den diese Website repräsentiert. Die Daten aus dem Benutzerprofil dieses Benutzers werden jetzt in den Suchergebnissen verwendet. %2$sAktualisiere dieses Benutzerprofil, um sicherzustellen, dass die Angaben korrekt sind.%3$s"],"Error: Please select a user below to make your site's meta data complete.":["Fehler: Bitte unten einen Benutzer auswählen, um die Metadaten deiner Website zu komplettieren."],"New step added":["Neuer Schritt hinzugefügt"],"New question added":["Neue Frage hinzugefügt"],"Did you know %s also analyzes the different word forms of your keyphrase, like plurals and past tenses?":["Wusstest du schon, dass %s auch Plural- oder Zeitformen deiner Keyphrase analysiert?"],"Help on choosing the perfect focus keyphrase":["Hilfe bei der Auswahl des perfekten Fokus-Schlüsselworts"],"Would you like to add a related keyphrase?":["Möchtest du eine verwandte Keyphrase hinzufügen?"],"Go %s!":["Start %s!"],"Rank better with synonyms & related keyphrases":["Ranke besser mit Synonymen & verwandten Keyphrasen."],"Add related keyphrase":["Ähnliches Keyword hinzufügen"],"Get %s":["Erhalte %s"],"Focus keyphrase":["Fokus-Keyphrase"],"Learn more about the readability analysis":["Lerne mehr über die Lesbarkeitsanalyse"],"Describe the duration of the instruction:":["Beschreibe die Dauer der Anleitung:"],"Optional. Customize how you want to describe the duration of the instruction":["Optional. Passe an, wie du die Dauer der Anleitung beschreiben möchtest. "],"%s, %s and %s":["%s, %s und %s"],"%s and %s":["%s und %s"],"%d minute":["%d Minute","%d Minuten"],"%d hour":["%d Stunde","%d Stunden"],"%d day":["%d Tag","%d Tage"],"Enter a step title":["Schritt-Titel eingeben"],"Optional. This can give you better control over the styling of the steps.":["Optional. Dies kann dir eine bessere Kontrolle über das Styling der Schritte geben."],"CSS class(es) to apply to the steps":["CSS-Klasse(n), die auf die Schritte angewendet werden sollen"],"minutes":["Minuten"],"hours":["Stunden"],"days":["Tage"],"Create a How-to guide in an SEO-friendly way. You can only use one How-to block per post.":["Erstelle eine Anleitung auf SEO-freundliche Weise. Du kannst nur einen How-to-Absatz pro Beitrag verwenden."],"List your Frequently Asked Questions in an SEO-friendly way. You can only use one FAQ block per post.":["Liste deine häufig gestellten Fragen SEO-freundlich auf. Du kannst nur einen FAQ-Absatz pro Bericht verwenden."],"Copy error":["Fehler kopieren"],"An error occurred loading the %s primary taxonomy picker.":["Beim Laden des primären Taxonomie-Pickers %s ist ein Fehler aufgetreten."],"Time needed:":["Benötigte Zeit:"],"Move question down":["Frage nach unten verschieben"],"Move question up":["Frage nach oben verschieben"],"Insert question":["Frage hinzufügen"],"Delete question":["Frage löschen"],"Enter the answer to the question":["Antwort auf die Frage eingeben"],"Enter a question":["Gib eine Frage ein"],"Add question":["Frage hinzufügen"],"Frequently Asked Questions":["Häufig gestellte Fragen (FAQ)"],"Great news: you can, with %s!":["Tolle Neuigkeiten: Du kannst es, mit %s!"],"Select the primary %s":["Wähle die primären %s"],"Mark as cornerstone content":["Als Cornerstone-Inhalt markieren"],"Move step down":["Schritt nach unten verschieben"],"Move step up":["Schritt nach oben verschieben"],"Insert step":["Schritt einfügen"],"Delete step":["Schritt löschen"],"Add image":["Bild hinzufügen"],"Enter a step description":["Gib eine Beschreibung für den Schritt ein"],"Enter a description":["Gib eine Beschreibung ein"],"Unordered list":["Unsortierte Liste"],"Showing step items as an ordered list.":["Schritt-Elemente als geordnete Liste anzeigen."],"Showing step items as an unordered list":["Schritt-Elemente als ungeordnete Liste anzeigen"],"Add step":["Schritt hinzufügen"],"Delete total time":["Gesamtzeit löschen"],"Add total time":["Gesamtzeit hinzufügen"],"How to":["Anleitung"],"How-to":["Anleitung"],"Analysis results":["Analyse-Ergebnisse"],"Enter a focus keyphrase to calculate the SEO score":["Gib ein Fokus-Keyword ein, um den SEO-Wert zu berechnen"],"Learn more about Cornerstone Content.":["Erfahre mehr über Cornerstone-Inhalte."],"Cornerstone content should be the most important and extensive articles on your site.":["Cornerstone-Inhalte sollten die wichtigsten und umfassendsten Artikel deiner Seite sein."],"Add synonyms":["Synonyme hinzufügen"],"Would you like to add keyphrase synonyms?":["Möchtest du Keyphrase-Synonyme hinzufügen?"],"Current year":["Aktuelles Jahr"],"Page":["Seite"],"Tagline":["Untertitel"],"Modify your meta description by editing it right here":["Bearbeite direkt hier deine Meta-Beschreibung "],"ID":["ID"],"Separator":["Trennzeichen"],"Search phrase":["Suchwort"],"Term description":["Begriffsbeschreibung"],"Tag description":["Schlagwortbeschreibung"],"Category description":["Kategoriebeschreibung"],"Primary category":["Primäre Kategorie"],"Category":["Kategorie"],"Excerpt only":["Nur Auszug"],"Excerpt":["Textauszug"],"Site title":["Titel der Website"],"Parent title":["Titel der übergeordneten Seite"],"Date":["Datum"],"24/7 email support":["24/7 E-Mail-Support"],"SEO analysis":["SEO Analyse"],"Other benefits of %s for you:":["Andere Vorteile von %s für dich:"],"Cornerstone content":["Cornerstone-Inhalt"],"Superfast internal linking suggestions":["Superschnelle Vorschläge zur internen Verlinkung"],"Great news: you can, with %1$s!":["Großartige Neuigkeit: Du kannst es, mit %1$s!"],"1 year free support and updates included!":["1 Jahr kostenfreie Updates und Upgrades inbegriffen!"],"%1$sSocial media preview%2$s: Facebook & Twitter":["%1$sSocial Media Vorschau%2$s: Facebook & Twitter"],"%1$sNo more dead links%2$s: easy redirect manager":["%1$sKeine verwaisten Links mehr%2$s: Einfacher Redirect-Manager"],"No ads!":["Keine Werbung!"],"Please provide a meta description by editing the snippet below.":["Bitte lege eine Meta-Beschreibung fest, indem du den Code-Schnipsel bearbeitest."],"The name of the person":["Der Name der Person"],"Readability analysis":["Lesbarkeits-Analyse"],"Open":["Offen"],"Title":["Titel"],"Close":["Schließen"],"FAQ":["FAQ"],"Settings":["Einstellungen"]}}}
1
+ {"domain":"wordpress-seo","locale_data":{"wordpress-seo":{"":{"domain":"wordpress-seo","plural-forms":"nplurals=2; plural=n != 1;","lang":"de"},"Google preview":["Google-Vorschau"],"When you click OK we will open our HelpScout beacon where you can find answers to your questions. This beacon will load our support data and also potentially set cookies.":["Wenn du auf OK klickst, öffnen wir unseren HelpScout-Beacon, in dem du Antworten auf deine Fragen finden kannst. Dieses Beacon lädt unsere Supportdaten und setzt möglicherweise Cookies."],"Schema":["Schema"],"Truly optimize your site for a local audience with our %s plugin! Optimized address details, opening hours, store locator and pickup option!":["Optimiere deine Website für ein lokales Publikum mit unserem %s-Plugin! Optimierte Adressdaten, Öffnungszeiten, Filialfinder und Abhol-Option!"],"Serving local customers?":["Betreuung lokaler Kunden?"],"Get the %s plugin now":["Hol dir jetzt das %s-Plugin"],"You can edit the details shown in meta data, like the social profiles, the name and the description of this user on their %1$s profile page.":["Du kannst die Angaben zu sozialen Profilen, den Namen und die Beschreibung dieses Benutzers, die in den Metadaten sichtbar sind, auf dessen %1$s Profilseite bearbeiten."],"Select a user...":["Wähle einen Benutzer…"],"Name:":["Name:"],"You have selected the user %1$s as the person this site represents. Their user profile information will now be used in search results. %2$sUpdate their profile to make sure the information is correct.%3$s":["Du hast den Benutzer %1$s als die Person ausgewählt, den diese Website repräsentiert. Die Daten aus dem Benutzerprofil dieses Benutzers werden jetzt in den Suchergebnissen verwendet. %2$sAktualisiere dieses Benutzerprofil, um sicherzustellen, dass die Angaben korrekt sind.%3$s"],"Error: Please select a user below to make your site's meta data complete.":["Fehler: Bitte unten einen Benutzer auswählen, um die Metadaten deiner Website zu komplettieren."],"New step added":["Neuer Schritt hinzugefügt"],"New question added":["Neue Frage hinzugefügt"],"Did you know %s also analyzes the different word forms of your keyphrase, like plurals and past tenses?":["Wusstest du schon, dass %s auch Plural- oder Zeitformen deiner Keyphrase analysiert?"],"Help on choosing the perfect focus keyphrase":["Hilfe bei der Auswahl des perfekten Fokus-Schlüsselworts"],"Would you like to add a related keyphrase?":["Möchtest du eine verwandte Keyphrase hinzufügen?"],"Go %s!":["Start %s!"],"Rank better with synonyms & related keyphrases":["Ranke besser mit Synonymen & verwandten Keyphrasen."],"Add related keyphrase":["Ähnliches Keyword hinzufügen"],"Get %s":["Erhalte %s"],"Focus keyphrase":["Fokus-Keyphrase"],"Learn more about the readability analysis":["Lerne mehr über die Lesbarkeitsanalyse"],"Describe the duration of the instruction:":["Beschreibe die Dauer der Anleitung:"],"Optional. Customize how you want to describe the duration of the instruction":["Optional. Passe an, wie du die Dauer der Anleitung beschreiben möchtest. "],"%s, %s and %s":["%s, %s und %s"],"%s and %s":["%s und %s"],"%d minute":["%d Minute","%d Minuten"],"%d hour":["%d Stunde","%d Stunden"],"%d day":["%d Tag","%d Tage"],"Enter a step title":["Schritt-Titel eingeben"],"Optional. This can give you better control over the styling of the steps.":["Optional. Dies kann dir eine bessere Kontrolle über das Styling der Schritte geben."],"CSS class(es) to apply to the steps":["CSS-Klasse(n), die auf die Schritte angewendet werden sollen"],"minutes":["Minuten"],"hours":["Stunden"],"days":["Tage"],"Create a How-to guide in an SEO-friendly way. You can only use one How-to block per post.":["Erstelle eine Anleitung auf SEO-freundliche Weise. Du kannst nur einen How-to-Absatz pro Beitrag verwenden."],"List your Frequently Asked Questions in an SEO-friendly way. You can only use one FAQ block per post.":["Liste deine häufig gestellten Fragen SEO-freundlich auf. Du kannst nur einen FAQ-Absatz pro Bericht verwenden."],"Copy error":["Fehler kopieren"],"An error occurred loading the %s primary taxonomy picker.":["Beim Laden des primären Taxonomie-Pickers %s ist ein Fehler aufgetreten."],"Time needed:":["Benötigte Zeit:"],"Move question down":["Frage nach unten verschieben"],"Move question up":["Frage nach oben verschieben"],"Insert question":["Frage hinzufügen"],"Delete question":["Frage löschen"],"Enter the answer to the question":["Antwort auf die Frage eingeben"],"Enter a question":["Gib eine Frage ein"],"Add question":["Frage hinzufügen"],"Frequently Asked Questions":["Häufig gestellte Fragen (FAQ)"],"Great news: you can, with %s!":["Tolle Neuigkeiten: Du kannst es, mit %s!"],"Select the primary %s":["Wähle die primären %s"],"Mark as cornerstone content":["Als Cornerstone-Inhalt markieren"],"Move step down":["Schritt nach unten verschieben"],"Move step up":["Schritt nach oben verschieben"],"Insert step":["Schritt einfügen"],"Delete step":["Schritt löschen"],"Add image":["Bild hinzufügen"],"Enter a step description":["Gib eine Beschreibung für den Schritt ein"],"Enter a description":["Gib eine Beschreibung ein"],"Unordered list":["Unsortierte Liste"],"Showing step items as an ordered list.":["Schritt-Elemente als geordnete Liste anzeigen."],"Showing step items as an unordered list":["Schritt-Elemente als ungeordnete Liste anzeigen"],"Add step":["Schritt hinzufügen"],"Delete total time":["Gesamtzeit löschen"],"Add total time":["Gesamtzeit hinzufügen"],"How to":["Anleitung"],"How-to":["Anleitung"],"Analysis results":["Analyse-Ergebnisse"],"Enter a focus keyphrase to calculate the SEO score":["Gib ein Fokus-Keyword ein, um den SEO-Wert zu berechnen"],"Learn more about Cornerstone Content.":["Erfahre mehr über Cornerstone-Inhalte."],"Cornerstone content should be the most important and extensive articles on your site.":["Cornerstone-Inhalte sollten die wichtigsten und umfassendsten Artikel deiner Seite sein."],"Add synonyms":["Synonyme hinzufügen"],"Would you like to add keyphrase synonyms?":["Möchtest du Keyphrase-Synonyme hinzufügen?"],"Current year":["Aktuelles Jahr"],"Page":["Seite"],"Tagline":["Untertitel"],"Modify your meta description by editing it right here":["Bearbeite direkt hier deine Meta-Beschreibung "],"ID":["ID"],"Separator":["Trennzeichen"],"Search phrase":["Suchwort"],"Term description":["Begriffsbeschreibung"],"Tag description":["Schlagwortbeschreibung"],"Category description":["Kategoriebeschreibung"],"Primary category":["Primäre Kategorie"],"Category":["Kategorie"],"Excerpt only":["Nur Auszug"],"Excerpt":["Textauszug"],"Site title":["Titel der Website"],"Parent title":["Titel der übergeordneten Seite"],"Date":["Datum"],"24/7 email support":["24/7 E-Mail-Support"],"SEO analysis":["SEO Analyse"],"Other benefits of %s for you:":["Andere Vorteile von %s für dich:"],"Cornerstone content":["Cornerstone-Inhalt"],"Superfast internal linking suggestions":["Superschnelle Vorschläge zur internen Verlinkung"],"Great news: you can, with %1$s!":["Großartige Neuigkeit: Du kannst es, mit %1$s!"],"1 year free support and updates included!":["1 Jahr kostenfreie Updates und Upgrades inbegriffen!"],"%1$sSocial media preview%2$s: Facebook & Twitter":["%1$sSocial Media Vorschau%2$s: Facebook & Twitter"],"%1$sNo more dead links%2$s: easy redirect manager":["%1$sKeine verwaisten Links mehr%2$s: Einfacher Redirect-Manager"],"No ads!":["Keine Werbung!"],"Please provide a meta description by editing the snippet below.":["Bitte lege eine Meta-Beschreibung fest, indem du den Code-Schnipsel bearbeitest."],"The name of the person":["Der Name der Person"],"Readability analysis":["Lesbarkeits-Analyse"],"Open":["Offen"],"Title":["Titel"],"Close":["Schließen"],"FAQ":["FAQ"],"Settings":["Einstellungen"]}}}
languages/yoast-components-de_DE.json CHANGED
@@ -1 +1 @@
1
- {"domain":"yoast-components","locale_data":{"yoast-components":{"":{"domain":"yoast-components","plural-forms":"nplurals=2; plural=n != 1;","lang":"de"},"Preview as:":["Vorschau als:"],"Mobile result":[],"Desktop result":[],"Dismiss this alert":["Hinweis ausblenden"],"The following words and word combinations occur the most in the content. These give an indication of what your content focuses on. If the words differ a lot from your topic, you might want to rewrite your content accordingly. ":["Die folgenden Wörter und Wort-Kombinationen kommen im Inhalt am häufigsten vor. Diese geben einen Hinweis darauf, worauf sich dein Inhalt konzentriert. Wenn sich die Wörter stark von deinem Thema unterscheiden, möchtest du vielleicht deinen Inhalt entsprechend umschreiben. "],"Once you add a bit more copy, we'll give you a list of words that occur the most in the content. These give an indication of what your content focuses on.":["Hast du deiner Seite einmal etwas mehr Inhalt hinzugefügt, werden wir dir eine Liste mit Wörtern anzeigen, die am meisten im Inhalt vorkommen. Dies kann eine gute Hilfestellung sein, um herauszufinden, worauf sich dein Inhalt konzentriert."],"%d occurrences":["%d Vorkommen"],"We could not find any relevant articles on your website that you could link to from your post.":["Wir konnten keine relevanten Beiträge auf deiner Website finden, auf die du von deinem Beitrag aus verlinken könntet."],"The image you selected is too small for Facebook":["Das ausgewählte Bild ist zu klein für Facebook."],"The given image url cannot be loaded":["Die angegebene URL zum Bild konnte nicht geladen werden."],"This is a list of related content to which you could link in your post. {{a}}Read our article about site structure{{/a}} to learn more about how internal linking can help improve your SEO.":["Dies ist eine Liste von verwandtem Inhalt auf den du in deinem Beitrag verweisen kannst. {{a}}Lies unseren Artikel über Seitenstruktur{{/a}}, um mehr darüber zu lernen, wie interne Verlinkungen deinen SEO Score verbessern können."],"Are you trying to use multiple keyphrases? You should add them separately below.":["Versuchst du, mehrere Keywords zu verwenden? Du solltest sie unten einzeln hinzufügen."],"Mark as cornerstone content":["Als Cornerstone-Inhalt markieren"],"image preview":["Bildvorschau"],"Copied!":["Kopiert!"],"Not supported!":["Nicht unterstützt!"],"Read {{a}}our article about site structure{{/a}} to learn more about how internal linking can help improve your SEO.":["Lies {{a}}unseren Artikel über Seitenstruktur{{/a}}, um mehr darüber zu erfahren, wie interne Verlinkungen deinen SEO Score verbessern können."],"Once you add a bit more copy, we'll give you a list of related content here to which you could link in your post.":["Hast du deiner Seite einmal etwas mehr Inhalt hinzugefügt, werden wir dir eine Liste mit verwandten Inhalten anzeigen, welche du in deinem Beitrag verlinken kannst."],"Consider linking to these {{a}}cornerstone articles:{{/a}}":["Überlege, zu diesen {{a}}Cornerstone-Artikeln{{/a}} zu verlinken. "],"Consider linking to these articles:":["Überlege, auf diese Artikel zu verlinken "],"Copy link":["Link kopieren"],"Copy link to suggested article: %s":["Link zum vorgeschlagenen Artikel kopieren: %s"],"Read our %1$sultimate guide to keyword research%2$s to learn more about keyword research and keyword strategy.":["Lies unser %1$sultimatives Handbuch zur Keyword-Recherche%2$s, um mehr über Keyword-Recherche und Keyword-Strategie zu erfahren."],"Once you add a bit more copy, we'll give you a list of words and word combinations that occur the most in the content. These give an indication of what your content focuses on.":["Hast du deiner Seite einmal etwas mehr Inhalt hinzugefügt, werden wir dir eine Liste mit Wörtern und Wort-Kombinationen anzeigen, die am meisten im Inhalt vorkommen. Dies kann eine gute Hilfestellung sein, um herauszufinden, worauf sich dein Inhalt konzentriert."],"The following words occur the most in the content. These give an indication of what your content focuses on. If the words differ a lot from your topic, you might want to rewrite your content accordingly. ":["Die folgenden Wörter kommen im Inhalt am häufigsten vor. Diese geben einen Hinweis darauf, worauf sich dein Inhalt konzentriert. Wenn sich die Wörter stark von deinem Thema unterscheiden, möchtest du vielleicht deinen Inhalt entsprechend umschreiben. "],"Prominent words":["Prominente Wörter "],"Something went wrong. Please reload the page.":["Das hat nicht funktioniert. Bitte lade die Seite neu."],"Modify your meta description by editing it right here":["Bearbeite direkt hier deine Meta-Beschreibung "],"Url preview":["URL-Vorschau"],"Please provide a meta description by editing the snippet below. If you don’t, Google will try to find a relevant part of your post to show in the search results.":["Bitte bearbeite das Codeschnipsel und richte eine Meta-Beschreibung ein. Wenn du dies nicht tust, wird Google selbständig versuchen, einen relevanten Teil deines Beitrags in den Suchergebnissen anzuzeigen."],"Insert snippet variable":["Codeschnipsel-Variable einsetzen"],"Dismiss this notice":["Ignoriere diese Nachricht"],"No results":["Keine Ergebnisse"],"%d result found, use up and down arrow keys to navigate":["%d Ergebnis gefunden, mit den Pfeiltasten nach oben und unten navigieren","%d Ergebnisse gefunden, mit den Pfeiltasten nach oben und unten navigieren"],"Your site language is set to %s. If this is not correct, contact your site administrator.":["Die Sprache deiner Website ist auf %s eingestellt. Wenn dies nicht korrekt ist, wende dich an deinen Website-Administrator."],"On":["An"],"Off":["Aus"],"Good results":["Gute Ergebnisse"],"Remove highlight from the text":["Text-Markierung entfernen"],"Your site language is set to %s. ":["Die Sprache deiner Website ist auf %s eingestellt."],"Highlight this result in the text":["Markiere dieses Ergebnis im Text"],"Considerations":["Überlegungen"],"Errors":["Fehler"],"Change language":["Sprache ändern"],"(Opens in a new browser tab)":["(Öffnet in einem neuen Browser Tab)"],"Scroll to see the preview content.":["Scrolle, um die Vorschau zu sehen."],"Step %1$d: %2$s":["Schritt %1$d: %2$s"],"Close snippet editor":["Ausschnitt-Editor schließen"],"Slug":["Permalink"],"Marks are disabled in current view":["Markierungen sind in der aktuellen Ansicht deaktiviert."],"Choose an image":["Wähle ein Bild"],"Remove the image":["Bild entfernen"],"MailChimp signup failed:":["MailChimp-Registrierung fehlgeschlagen:"],"Sign Up!":["Anmelden"],"Edit snippet":["Code-Schnipsel bearbeiten"],"SEO title preview":["SEO-Titel Vorschau"],"Meta description preview":["Meta Description Vorschau"],"A problem occurred when saving the current step, {{link}}please file a bug report{{/link}} describing what step you are on and which changes you want to make (if any).":["Beim Speichern des aktuellen Schritts ist ein Problem aufgetreten. {{link}}Bitte erfasse einen Fehlerbericht{{/link}}, der beschreibt in welchen Schritt du warst und welche Änderungen du vorgenommen hast (falls Änderungen gemacht wurden)."],"Close the Wizard":["Schließe den Assistenten "],"%s installation wizard":["%s Installationsassistent"],"SEO title":["SEO Titel"],"Improvements":["Verbesserungen"],"Problems":["Probleme"],"Email":["E-Mail"],"Previous":["Zurück"],"Next":["Weiter"],"Close":["Schließen"],"Meta description":["Meta-Beschreibung"]}}}
1
+ {"domain":"yoast-components","locale_data":{"yoast-components":{"":{"domain":"yoast-components","plural-forms":"nplurals=2; plural=n != 1;","lang":"de"},"Preview as:":["Vorschau als:"],"Mobile result":["Ergebnis für die mobilen Geräte"],"Desktop result":["Ergebnis für den Desktop"],"Dismiss this alert":["Hinweis ausblenden"],"The following words and word combinations occur the most in the content. These give an indication of what your content focuses on. If the words differ a lot from your topic, you might want to rewrite your content accordingly. ":["Die folgenden Wörter und Wort-Kombinationen kommen im Inhalt am häufigsten vor. Diese geben einen Hinweis darauf, worauf sich dein Inhalt konzentriert. Wenn sich die Wörter stark von deinem Thema unterscheiden, möchtest du vielleicht deinen Inhalt entsprechend umschreiben. "],"Once you add a bit more copy, we'll give you a list of words that occur the most in the content. These give an indication of what your content focuses on.":["Hast du deiner Seite einmal etwas mehr Inhalt hinzugefügt, werden wir dir eine Liste mit Wörtern anzeigen, die am meisten im Inhalt vorkommen. Dies kann eine gute Hilfestellung sein, um herauszufinden, worauf sich dein Inhalt konzentriert."],"%d occurrences":["%d Vorkommen"],"We could not find any relevant articles on your website that you could link to from your post.":["Wir konnten keine relevanten Beiträge auf deiner Website finden, auf die du von deinem Beitrag aus verlinken könntet."],"The image you selected is too small for Facebook":["Das ausgewählte Bild ist zu klein für Facebook."],"The given image url cannot be loaded":["Die angegebene URL zum Bild konnte nicht geladen werden."],"This is a list of related content to which you could link in your post. {{a}}Read our article about site structure{{/a}} to learn more about how internal linking can help improve your SEO.":["Dies ist eine Liste von verwandtem Inhalt auf den du in deinem Beitrag verweisen kannst. {{a}}Lies unseren Artikel über Seitenstruktur{{/a}}, um mehr darüber zu lernen, wie interne Verlinkungen deinen SEO Score verbessern können."],"Are you trying to use multiple keyphrases? You should add them separately below.":["Versuchst du, mehrere Keywords zu verwenden? Du solltest sie unten einzeln hinzufügen."],"Mark as cornerstone content":["Als Cornerstone-Inhalt markieren"],"image preview":["Bildvorschau"],"Copied!":["Kopiert!"],"Not supported!":["Nicht unterstützt!"],"Read {{a}}our article about site structure{{/a}} to learn more about how internal linking can help improve your SEO.":["Lies {{a}}unseren Artikel über Seitenstruktur{{/a}}, um mehr darüber zu erfahren, wie interne Verlinkungen deinen SEO Score verbessern können."],"Once you add a bit more copy, we'll give you a list of related content here to which you could link in your post.":["Hast du deiner Seite einmal etwas mehr Inhalt hinzugefügt, werden wir dir eine Liste mit verwandten Inhalten anzeigen, welche du in deinem Beitrag verlinken kannst."],"Consider linking to these {{a}}cornerstone articles:{{/a}}":["Überlege, zu diesen {{a}}Cornerstone-Artikeln{{/a}} zu verlinken. "],"Consider linking to these articles:":["Überlege, auf diese Artikel zu verlinken "],"Copy link":["Link kopieren"],"Copy link to suggested article: %s":["Link zum vorgeschlagenen Artikel kopieren: %s"],"Read our %1$sultimate guide to keyword research%2$s to learn more about keyword research and keyword strategy.":["Lies unser %1$sultimatives Handbuch zur Keyword-Recherche%2$s, um mehr über Keyword-Recherche und Keyword-Strategie zu erfahren."],"Once you add a bit more copy, we'll give you a list of words and word combinations that occur the most in the content. These give an indication of what your content focuses on.":["Hast du deiner Seite einmal etwas mehr Inhalt hinzugefügt, werden wir dir eine Liste mit Wörtern und Wort-Kombinationen anzeigen, die am meisten im Inhalt vorkommen. Dies kann eine gute Hilfestellung sein, um herauszufinden, worauf sich dein Inhalt konzentriert."],"The following words occur the most in the content. These give an indication of what your content focuses on. If the words differ a lot from your topic, you might want to rewrite your content accordingly. ":["Die folgenden Wörter kommen im Inhalt am häufigsten vor. Diese geben einen Hinweis darauf, worauf sich dein Inhalt konzentriert. Wenn sich die Wörter stark von deinem Thema unterscheiden, möchtest du vielleicht deinen Inhalt entsprechend umschreiben. "],"Prominent words":["Prominente Wörter "],"Something went wrong. Please reload the page.":["Das hat nicht funktioniert. Bitte lade die Seite neu."],"Modify your meta description by editing it right here":["Bearbeite direkt hier deine Meta-Beschreibung "],"Url preview":["URL-Vorschau"],"Please provide a meta description by editing the snippet below. If you don’t, Google will try to find a relevant part of your post to show in the search results.":["Bitte bearbeite das Codeschnipsel und richte eine Meta-Beschreibung ein. Wenn du dies nicht tust, wird Google selbständig versuchen, einen relevanten Teil deines Beitrags in den Suchergebnissen anzuzeigen."],"Insert snippet variable":["Codeschnipsel-Variable einsetzen"],"Dismiss this notice":["Ignoriere diese Nachricht"],"No results":["Keine Ergebnisse"],"%d result found, use up and down arrow keys to navigate":["%d Ergebnis gefunden, mit den Pfeiltasten nach oben und unten navigieren","%d Ergebnisse gefunden, mit den Pfeiltasten nach oben und unten navigieren"],"Your site language is set to %s. If this is not correct, contact your site administrator.":["Die Sprache deiner Website ist auf %s eingestellt. Wenn dies nicht korrekt ist, wende dich an deinen Website-Administrator."],"On":["An"],"Off":["Aus"],"Good results":["Gute Ergebnisse"],"Remove highlight from the text":["Text-Markierung entfernen"],"Your site language is set to %s. ":["Die Sprache deiner Website ist auf %s eingestellt."],"Highlight this result in the text":["Markiere dieses Ergebnis im Text"],"Considerations":["Überlegungen"],"Errors":["Fehler"],"Change language":["Sprache ändern"],"(Opens in a new browser tab)":["(Öffnet in einem neuen Browser Tab)"],"Scroll to see the preview content.":["Scrolle, um die Vorschau zu sehen."],"Step %1$d: %2$s":["Schritt %1$d: %2$s"],"Close snippet editor":["Ausschnitt-Editor schließen"],"Slug":["Permalink"],"Marks are disabled in current view":["Markierungen sind in der aktuellen Ansicht deaktiviert."],"Choose an image":["Wähle ein Bild"],"Remove the image":["Bild entfernen"],"MailChimp signup failed:":["MailChimp-Registrierung fehlgeschlagen:"],"Sign Up!":["Anmelden"],"Edit snippet":["Code-Schnipsel bearbeiten"],"SEO title preview":["SEO-Titel Vorschau"],"Meta description preview":["Meta Description Vorschau"],"A problem occurred when saving the current step, {{link}}please file a bug report{{/link}} describing what step you are on and which changes you want to make (if any).":["Beim Speichern des aktuellen Schritts ist ein Problem aufgetreten. {{link}}Bitte erfasse einen Fehlerbericht{{/link}}, der beschreibt in welchen Schritt du warst und welche Änderungen du vorgenommen hast (falls Änderungen gemacht wurden)."],"Close the Wizard":["Schließe den Assistenten "],"%s installation wizard":["%s Installationsassistent"],"SEO title":["SEO Titel"],"Improvements":["Verbesserungen"],"Problems":["Probleme"],"Email":["E-Mail"],"Previous":["Zurück"],"Next":["Weiter"],"Close":["Schließen"],"Meta description":["Meta-Beschreibung"]}}}
src/orm/yoast-model.php → lib/model.php RENAMED
@@ -1,13 +1,11 @@
1
  <?php
2
  /**
3
- * Yoast extension of the Model class.
4
  *
5
- * @package Yoast\YoastSEO\ORM
6
  */
7
 
8
- namespace Yoast\WP\SEO\ORM;
9
-
10
- use Yoast\WP\SEO\Exceptions\Missing_Method;
11
 
12
  /**
13
  * Make Model compatible with WordPress.
@@ -18,18 +16,8 @@ use Yoast\WP\SEO\Exceptions\Missing_Method;
18
  * class Widget extends Model {
19
  * }
20
  *
21
- * The methods documented below are magic methods that conform to PSR-1.
22
- * This documentation exposes these methods to doc generators and IDEs.
23
- *
24
- * @link http://www.php-fig.org/psr/psr-1/
25
- *
26
- * @method void setOrm($orm)
27
- * @method $this setExpr($property, $value = null)
28
- * @method bool isDirty($property)
29
- * @method bool isNew()
30
- * @method Array asArray()
31
  */
32
- class Yoast_Model {
33
 
34
  /**
35
  * Default ID column for all models. Can be overridden by adding
@@ -57,13 +45,6 @@ class Yoast_Model {
57
  */
58
  public static $auto_prefix_models = '\Yoast\WP\SEO\Models\\';
59
 
60
- /**
61
- * Set a logger to use for all models.
62
- *
63
- * @var \YoastSEO_Vendor\Psr\Log\LoggerInterface $logger
64
- */
65
- public static $logger;
66
-
67
  /**
68
  * Set true to to ignore namespace information when computing table names
69
  * from class names.
@@ -78,7 +59,7 @@ class Yoast_Model {
78
  /**
79
  * The ORM instance used by this model instance to communicate with the database.
80
  *
81
- * @var \YoastSEO_Vendor\ORM $orm
82
  */
83
  public $orm;
84
 
@@ -116,7 +97,7 @@ class Yoast_Model {
116
  * @param string $class_name Type of Model to load.
117
  * @param bool $yoast_prefix Optional. True to prefix the table name with the Yoast prefix.
118
  *
119
- * @return \Yoast\WP\SEO\ORM\ORMWrapper Wrapper to use.
120
  */
121
  public static function of_type( $class_name, $yoast_prefix = true ) {
122
  // Prepend namespace to the class name.
@@ -133,7 +114,7 @@ class Yoast_Model {
133
  *
134
  * @param string $class_name Type of Model to load.
135
  *
136
- * @return \Yoast\WP\SEO\ORM\ORMWrapper
137
  */
138
  public static function of_wp_type( $class_name ) {
139
  return static::of_type( $class_name, false );
@@ -316,18 +297,14 @@ class Yoast_Model {
316
  * responsible for returning instances of the correct class when
317
  * its find_one or find_many methods are called.
318
  *
319
- * @param string $class_name The target class name.
320
- * @param null|string $connection_name The name of the connection.
321
  *
322
- * @return \Yoast\WP\SEO\ORM\ORMWrapper Instance of the ORM wrapper.
323
  */
324
- public static function factory( $class_name, $connection_name = null ) {
325
  $class_name = static::$auto_prefix_models . $class_name;
326
  $table_name = static::get_table_name_for_class( $class_name );
327
- if ( $connection_name === null ) {
328
- $connection_name = static::get_static_property( $class_name, '_connection_name', ORMWrapper::DEFAULT_CONNECTION );
329
- }
330
- $wrapper = ORMWrapper::for_table( $table_name, $connection_name );
331
  $wrapper->set_class_name( $class_name );
332
  $wrapper->use_id_column( static::get_id_column_name( $class_name ) );
333
 
@@ -343,12 +320,11 @@ class Yoast_Model {
343
  * @param string $associated_class_name The associated class name.
344
  * @param null|string $foreign_key_name The foreign key name in the associated table.
345
  * @param null|string $foreign_key_name_in_current_models_table The foreign key in the current models table.
346
- * @param null|string $connection_name The name of the connection.
347
  *
348
- * @return \Yoast\WP\SEO\ORM\ORMWrapper
349
  * @throws \Exception When ID of current model has a null value.
350
  */
351
- protected function has_one_or_many( $associated_class_name, $foreign_key_name = null, $foreign_key_name_in_current_models_table = null, $connection_name = null ) {
352
  $base_table_name = static::get_table_name_for_class( \get_class( $this ) );
353
  $foreign_key_name = static::build_foreign_key_name( $foreign_key_name, $base_table_name );
354
 
@@ -365,7 +341,7 @@ class Yoast_Model {
365
  $where_value = $this->{$foreign_key_name_in_current_models_table};
366
  }
367
 
368
- return static::factory( $associated_class_name, $connection_name )->where( $foreign_key_name, $where_value );
369
  }
370
 
371
  /**
@@ -375,13 +351,12 @@ class Yoast_Model {
375
  * @param string $associated_class_name The associated class name.
376
  * @param null|string $foreign_key_name The foreign key name in the associated table.
377
  * @param null|string $foreign_key_name_in_current_models_table The foreign key in the current models table.
378
- * @param null|string $connection_name The name of the connection.
379
  *
380
- * @return \Yoast\WP\SEO\ORM\ORMWrapper Instance of the ORM.
381
  * @throws \Exception When ID of current model has a null value.
382
  */
383
- protected function has_one( $associated_class_name, $foreign_key_name = null, $foreign_key_name_in_current_models_table = null, $connection_name = null ) {
384
- return $this->has_one_or_many( $associated_class_name, $foreign_key_name, $foreign_key_name_in_current_models_table, $connection_name );
385
  }
386
 
387
  /**
@@ -391,15 +366,14 @@ class Yoast_Model {
391
  * @param string $associated_class_name The associated class name.
392
  * @param null|string $foreign_key_name The foreign key name in the associated table.
393
  * @param null|string $foreign_key_name_in_current_models_table The foreign key in the current models table.
394
- * @param null|string $connection_name The name of the connection.
395
  *
396
- * @return \Yoast\WP\SEO\ORM\ORMWrapper Instance of the ORM.
397
  * @throws \Exception When ID has a null value.
398
  */
399
- protected function has_many( $associated_class_name, $foreign_key_name = null, $foreign_key_name_in_current_models_table = null, $connection_name = null ) {
400
  $this->set_table_name( $associated_class_name );
401
 
402
- return $this->has_one_or_many( $associated_class_name, $foreign_key_name, $foreign_key_name_in_current_models_table, $connection_name );
403
  }
404
 
405
  /**
@@ -409,11 +383,10 @@ class Yoast_Model {
409
  * @param string $associated_class_name The associated class name.
410
  * @param null|string $foreign_key_name The foreign key in the current models table.
411
  * @param null|string $foreign_key_name_in_associated_models_table The foreign key in the associated table.
412
- * @param null|string $connection_name The name of the connection.
413
  *
414
  * @return $this|null Instance of the foreign model.
415
  */
416
- protected function belongs_to( $associated_class_name, $foreign_key_name = null, $foreign_key_name_in_associated_models_table = null, $connection_name = null ) {
417
  $this->set_table_name( $associated_class_name );
418
 
419
  $associated_table_name = static::get_table_name_for_class( static::$auto_prefix_models . $associated_class_name );
@@ -426,11 +399,11 @@ class Yoast_Model {
426
  *
427
  * NOTE: primary_key is a placeholder for the actual primary key column's name in $associated_table_name.
428
  */
429
- return static::factory( $associated_class_name, $connection_name )->where_id_is( $associated_object_id );
430
  }
431
 
432
  // Comparison: "{$associated_table_name}.{$foreign_key_name_in_associated_models_table} = {$associated_object_id}".
433
- return static::factory( $associated_class_name, $connection_name )->where( $foreign_key_name_in_associated_models_table, $associated_object_id );
434
  }
435
 
436
  /**
@@ -443,11 +416,10 @@ class Yoast_Model {
443
  * @param null|string $key_to_associated_table The key to the associated table.
444
  * @param null|string $key_in_base_table The key in the current models table.
445
  * @param null|string $key_in_associated_table The key in the associated table.
446
- * @param null|string $connection_name The name of the connection.
447
  *
448
- * @return \Yoast\WP\SEO\ORM\ORMWrapper Instance of the ORM.
449
  */
450
- protected function has_many_through( $associated_class_name, $join_class_name = null, $key_to_base_table = null, $key_to_associated_table = null, $key_in_base_table = null, $key_in_associated_table = null, $connection_name = null ) {
451
  $base_class_name = \get_class( $this );
452
 
453
  /*
@@ -491,7 +463,7 @@ class Yoast_Model {
491
  ON {$associated_table_name}.{$associated_table_id_column} = {$join_table_name}.{$key_to_associated_table}
492
  WHERE {$join_table_name}.{$key_to_base_table} = {$this->$base_table_id_column} ;"
493
  */
494
- return static::factory( $associated_class_name, $connection_name )
495
  ->select( "{$associated_table_name}.*" )
496
  ->join(
497
  $join_table_name,
@@ -507,7 +479,7 @@ class Yoast_Model {
507
  /**
508
  * Set the wrapped ORM instance associated with this Model instance.
509
  *
510
- * @param \YoastSEO_Vendor\ORM $orm The ORM instance to set.
511
  *
512
  * @return void
513
  */
@@ -696,7 +668,7 @@ class Yoast_Model {
696
  }
697
 
698
  /**
699
- * Calls static methods directly on the ORMWrapper
700
  *
701
  * @param string $method The method to call.
702
  * @param array $arguments The arguments to use.
@@ -712,28 +684,4 @@ class Yoast_Model {
712
 
713
  return \call_user_func_array( [ $model, $method ], $arguments );
714
  }
715
-
716
- /**
717
- * Magic method to capture calls to undefined class methods.
718
- * In this case we are attempting to convert camel case formatted
719
- * methods into underscore formatted methods.
720
- *
721
- * This allows us to call methods using camel case and remain
722
- * backwards compatible.
723
- *
724
- * @param string $name The method to call.
725
- * @param array $arguments The arguments to use.
726
- *
727
- * @throws \Yoast\WP\SEO\Exceptions\Missing_Method When the method does not exist.
728
- *
729
- * @return bool|\Yoast\WP\SEO\ORMWrapper Result of the call.
730
- */
731
- public function __call( $name, $arguments ) {
732
- $method = \strtolower( \preg_replace( '/([a-z])([A-Z])/', '$1_$2', $name ) );
733
- if ( ! \method_exists( $this, $method ) ) {
734
- throw Missing_Method::for_class( \get_class( $this ), $name );
735
- }
736
-
737
- return \call_user_func_array( [ $this, $method ], $arguments );
738
- }
739
  }
1
  <?php
2
  /**
3
+ * Yoast model class.
4
  *
5
+ * @package Yoast\WP\Lib
6
  */
7
 
8
+ namespace Yoast\WP\Lib;
 
 
9
 
10
  /**
11
  * Make Model compatible with WordPress.
16
  * class Widget extends Model {
17
  * }
18
  *
 
 
 
 
 
 
 
 
 
 
19
  */
20
+ class Model {
21
 
22
  /**
23
  * Default ID column for all models. Can be overridden by adding
45
  */
46
  public static $auto_prefix_models = '\Yoast\WP\SEO\Models\\';
47
 
 
 
 
 
 
 
 
48
  /**
49
  * Set true to to ignore namespace information when computing table names
50
  * from class names.
59
  /**
60
  * The ORM instance used by this model instance to communicate with the database.
61
  *
62
+ * @var ORM $orm
63
  */
64
  public $orm;
65
 
97
  * @param string $class_name Type of Model to load.
98
  * @param bool $yoast_prefix Optional. True to prefix the table name with the Yoast prefix.
99
  *
100
+ * @return ORM Wrapper to use.
101
  */
102
  public static function of_type( $class_name, $yoast_prefix = true ) {
103
  // Prepend namespace to the class name.
114
  *
115
  * @param string $class_name Type of Model to load.
116
  *
117
+ * @return ORM
118
  */
119
  public static function of_wp_type( $class_name ) {
120
  return static::of_type( $class_name, false );
297
  * responsible for returning instances of the correct class when
298
  * its find_one or find_many methods are called.
299
  *
300
+ * @param string $class_name The target class name.
 
301
  *
302
+ * @return ORM Instance of the ORM wrapper.
303
  */
304
+ public static function factory( $class_name ) {
305
  $class_name = static::$auto_prefix_models . $class_name;
306
  $table_name = static::get_table_name_for_class( $class_name );
307
+ $wrapper = ORM::for_table( $table_name );
 
 
 
308
  $wrapper->set_class_name( $class_name );
309
  $wrapper->use_id_column( static::get_id_column_name( $class_name ) );
310
 
320
  * @param string $associated_class_name The associated class name.
321
  * @param null|string $foreign_key_name The foreign key name in the associated table.
322
  * @param null|string $foreign_key_name_in_current_models_table The foreign key in the current models table.
 
323
  *
324
+ * @return ORM
325
  * @throws \Exception When ID of current model has a null value.
326
  */
327
+ protected function has_one_or_many( $associated_class_name, $foreign_key_name = null, $foreign_key_name_in_current_models_table = null ) {
328
  $base_table_name = static::get_table_name_for_class( \get_class( $this ) );
329
  $foreign_key_name = static::build_foreign_key_name( $foreign_key_name, $base_table_name );
330
 
341
  $where_value = $this->{$foreign_key_name_in_current_models_table};
342
  }
343
 
344
+ return static::factory( $associated_class_name )->where( $foreign_key_name, $where_value );
345
  }
346
 
347
  /**
351
  * @param string $associated_class_name The associated class name.
352
  * @param null|string $foreign_key_name The foreign key name in the associated table.
353
  * @param null|string $foreign_key_name_in_current_models_table The foreign key in the current models table.
 
354
  *
355
+ * @return ORM Instance of the ORM.
356
  * @throws \Exception When ID of current model has a null value.
357
  */
358
+ protected function has_one( $associated_class_name, $foreign_key_name = null, $foreign_key_name_in_current_models_table = null ) {
359
+ return $this->has_one_or_many( $associated_class_name, $foreign_key_name, $foreign_key_name_in_current_models_table );
360
  }
361
 
362
  /**
366
  * @param string $associated_class_name The associated class name.
367
  * @param null|string $foreign_key_name The foreign key name in the associated table.
368
  * @param null|string $foreign_key_name_in_current_models_table The foreign key in the current models table.
 
369
  *
370
+ * @return ORM Instance of the ORM.
371
  * @throws \Exception When ID has a null value.
372
  */
373
+ protected function has_many( $associated_class_name, $foreign_key_name = null, $foreign_key_name_in_current_models_table = null ) {
374
  $this->set_table_name( $associated_class_name );
375
 
376
+ return $this->has_one_or_many( $associated_class_name, $foreign_key_name, $foreign_key_name_in_current_models_table );
377
  }
378
 
379
  /**
383
  * @param string $associated_class_name The associated class name.
384
  * @param null|string $foreign_key_name The foreign key in the current models table.
385
  * @param null|string $foreign_key_name_in_associated_models_table The foreign key in the associated table.
 
386
  *
387
  * @return $this|null Instance of the foreign model.
388
  */
389
+ protected function belongs_to( $associated_class_name, $foreign_key_name = null, $foreign_key_name_in_associated_models_table = null ) {
390
  $this->set_table_name( $associated_class_name );
391
 
392
  $associated_table_name = static::get_table_name_for_class( static::$auto_prefix_models . $associated_class_name );
399
  *
400
  * NOTE: primary_key is a placeholder for the actual primary key column's name in $associated_table_name.
401
  */
402
+ return static::factory( $associated_class_name )->where_id_is( $associated_object_id );
403
  }
404
 
405
  // Comparison: "{$associated_table_name}.{$foreign_key_name_in_associated_models_table} = {$associated_object_id}".
406
+ return static::factory( $associated_class_name )->where( $foreign_key_name_in_associated_models_table, $associated_object_id );
407
  }
408
 
409
  /**
416
  * @param null|string $key_to_associated_table The key to the associated table.
417
  * @param null|string $key_in_base_table The key in the current models table.
418
  * @param null|string $key_in_associated_table The key in the associated table.
 
419
  *
420
+ * @return ORM Instance of the ORM.
421
  */
422
+ protected function has_many_through( $associated_class_name, $join_class_name = null, $key_to_base_table = null, $key_to_associated_table = null, $key_in_base_table = null, $key_in_associated_table = null ) {
423
  $base_class_name = \get_class( $this );
424
 
425
  /*
463
  ON {$associated_table_name}.{$associated_table_id_column} = {$join_table_name}.{$key_to_associated_table}
464
  WHERE {$join_table_name}.{$key_to_base_table} = {$this->$base_table_id_column} ;"
465
  */
466
+ return static::factory( $associated_class_name )
467
  ->select( "{$associated_table_name}.*" )
468
  ->join(
469
  $join_table_name,
479
  /**
480
  * Set the wrapped ORM instance associated with this Model instance.
481
  *
482
+ * @param ORM $orm The ORM instance to set.
483
  *
484
  * @return void
485
  */
668
  }
669
 
670
  /**
671
+ * Calls static methods directly on the ORM
672
  *
673
  * @param string $method The method to call.
674
  * @param array $arguments The arguments to use.
684
 
685
  return \call_user_func_array( [ $model, $method ], $arguments );
686
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
687
  }
lib/orm.php ADDED
@@ -0,0 +1,2342 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Yoast ORM class.
4
+ *
5
+ * @package Yoast\WP\Lib
6
+ */
7
+
8
+ namespace Yoast\WP\Lib;
9
+
10
+ use wpdb;
11
+
12
+ /**
13
+ *
14
+ * Based on Idiorm
15
+ *
16
+ * http://github.com/j4mie/idiorm/
17
+ *
18
+ * A single-class super-simple database abstraction layer for PHP.
19
+ * Provides (nearly) zero-configuration object-relational mapping
20
+ * and a fluent interface for building basic, commonly-used queries.
21
+ *
22
+ * BSD Licensed.
23
+ *
24
+ * Copyright (c) 2010, Jamie Matthews
25
+ * All rights reserved.
26
+ *
27
+ * Redistribution and use in source and binary forms, with or without
28
+ * modification, are permitted provided that the following conditions are met:
29
+ *
30
+ * * Redistributions of source code must retain the above copyright notice, this
31
+ * list of conditions and the following disclaimer.
32
+ *
33
+ * * Redistributions in binary form must reproduce the above copyright notice,
34
+ * this list of conditions and the following disclaimer in the documentation
35
+ * and/or other materials provided with the distribution.
36
+ *
37
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
38
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
39
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
41
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
42
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
43
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
44
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
46
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
47
+ *
48
+ *
49
+ * The methods documented below are magic methods that conform to PSR-1.
50
+ * This documentation exposes these methods to doc generators and IDEs.
51
+ *
52
+ * @see http://www.php-fig.org/psr/psr-1/
53
+ */
54
+ class ORM implements \ArrayAccess {
55
+
56
+ // ----------------------- //
57
+ // --- CLASS CONSTANTS --- //
58
+ // ----------------------- //
59
+ // WHERE and HAVING condition array keys
60
+ const CONDITION_FRAGMENT = 0;
61
+ const CONDITION_VALUES = 1;
62
+ // --------------------------- //
63
+ // --- INSTANCE PROPERTIES --- //
64
+ // --------------------------- //
65
+ /**
66
+ * The wrapped find_one and find_many classes will return an instance or
67
+ * instances of this class.
68
+ *
69
+ * @var string
70
+ */
71
+ protected $class_name;
72
+ /**
73
+ * The name of the table the current ORM instance is associated with.
74
+ *
75
+ * @var string
76
+ */
77
+ protected $_table_name;
78
+
79
+ /**
80
+ * Alias for the table to be used in SELECT queries
81
+ *
82
+ * @var string
83
+ */
84
+ protected $_table_alias = null;
85
+
86
+ /**
87
+ * Values to be bound to the query
88
+ *
89
+ * @var array
90
+ */
91
+ protected $_values = [];
92
+
93
+ /**
94
+ * Columns to select in the result
95
+ *
96
+ * @var array
97
+ */
98
+ protected $_result_columns = [ '*' ];
99
+
100
+ /**
101
+ * Are we using the default result column or have these been manually changed?
102
+ *
103
+ * @var boolean
104
+ */
105
+ protected $_using_default_result_columns = true;
106
+
107
+ /**
108
+ * Join sources
109
+ *
110
+ * @var array
111
+ */
112
+ protected $_join_sources = [];
113
+
114
+ /**
115
+ * Should the query include a DISTINCT keyword?
116
+ *
117
+ * @var boolean
118
+ */
119
+ protected $_distinct = false;
120
+
121
+ /**
122
+ * Is this a raw query?
123
+ *
124
+ * @var boolean
125
+ */
126
+ protected $_is_raw_query = false;
127
+
128
+ /**
129
+ * The raw query
130
+ *
131
+ * @var string
132
+ */
133
+ protected $_raw_query = '';
134
+
135
+ /**
136
+ * The raw query parameters
137
+ *
138
+ * @var array
139
+ */
140
+ protected $_raw_parameters = [];
141
+
142
+ /**
143
+ * Array of WHERE clauses
144
+ *
145
+ * @var array
146
+ */
147
+ protected $_where_conditions = [];
148
+
149
+ /**
150
+ * LIMIT
151
+ *
152
+ * @var int
153
+ */
154
+ protected $_limit = null;
155
+
156
+ /**
157
+ * OFFSET
158
+ *
159
+ * @var int
160
+ */
161
+ protected $_offset = null;
162
+
163
+ /**
164
+ * ORDER BY
165
+ *
166
+ * @var array
167
+ */
168
+ protected $_order_by = [];
169
+
170
+ /**
171
+ * GROUP BY
172
+ *
173
+ * @var array
174
+ */
175
+ protected $_group_by = [];
176
+
177
+ /**
178
+ * HAVING
179
+ *
180
+ * @var array
181
+ */
182
+ protected $_having_conditions = [];
183
+
184
+ /**
185
+ * The data for a hydrated instance of the class
186
+ *
187
+ * @var array
188
+ */
189
+ protected $_data = [];
190
+
191
+ /**
192
+ * lifetime of the object
193
+ *
194
+ * @var array
195
+ */
196
+ protected $_dirty_fields = [];
197
+
198
+ /**
199
+ * Fields that are to be inserted in the DB raw
200
+ *
201
+ * @var array
202
+ */
203
+ protected $_expr_fields = [];
204
+
205
+ /**
206
+ * Is this a new object (has create() been called)?
207
+ *
208
+ * @var boolean
209
+ */
210
+ protected $_is_new = false;
211
+
212
+ /**
213
+ * Name of the column to use as the primary key for
214
+ * this instance only. Overrides the config settings.
215
+ *
216
+ * @var string
217
+ */
218
+ protected $_instance_id_column = null;
219
+ // ---------------------- //
220
+ // --- STATIC METHODS --- //
221
+ // ---------------------- //
222
+ /**
223
+ * Factory method, return an instance of this class bound to the supplied
224
+ * table name.
225
+ *
226
+ * A repeat of content in parent::for_table, so that created class is
227
+ * ORMWrapper, not ORM.
228
+ *
229
+ * @param string $table_name Table name. The table to create instance for.
230
+ *
231
+ * @return ORM Instance of the ORM wrapper.
232
+ */
233
+ public static function for_table( $table_name ) {
234
+ return new static( $table_name, [] );
235
+ }
236
+
237
+ /**
238
+ * Executes a raw query as a wrapper for wpdb::query.
239
+ * Useful for queries that can't be accomplished through Idiorm,
240
+ * particularly those using engine-specific features.
241
+ *
242
+ * @param string $query The raw SQL query
243
+ * @param array $parameters Optional bound parameters
244
+ *
245
+ * @return bool Success
246
+ * @example raw_execute('SELECT `name`, AVG(`order`) FROM `customer` GROUP BY `name` HAVING AVG(`order`) > 10')
247
+ *
248
+ * @example raw_execute('INSERT OR REPLACE INTO `widget` (`id`, `name`) SELECT `id`, `name` FROM `other_table`')
249
+ */
250
+ public static function raw_execute( $query, $parameters = [] ) {
251
+ return self::_execute( $query, $parameters );
252
+ }
253
+
254
+ /**
255
+ * Internal helper method for executing statements.
256
+ *
257
+ * @param string $query The query.
258
+ * @param array $parameters An array of parameters to be bound in to the query.
259
+ *
260
+ * @return bool|int Response of wpdb::query
261
+ */
262
+ protected static function _execute( $query, $parameters = [] ) {
263
+ /**
264
+ * @var wpdb wpdb
265
+ */
266
+ global $wpdb;
267
+
268
+ $query = $wpdb->prepare( $query, $parameters );
269
+
270
+ return $wpdb->query( $query );
271
+ }
272
+
273
+ // ------------------------ //
274
+ // --- INSTANCE METHODS --- //
275
+ // ------------------------ //
276
+ /**
277
+ * "Private" constructor; shouldn't be called directly.
278
+ * Use the ORM::for_table factory method instead.
279
+ *
280
+ * @param string $table_name Table name.
281
+ * @param array $data Data to populate table.
282
+ */
283
+ protected function __construct( $table_name, $data = [] ) {
284
+ $this->_table_name = $table_name;
285
+ $this->_data = $data;
286
+ }
287
+
288
+ /**
289
+ * Set the name of the class which the wrapped methods should return
290
+ * instances of.
291
+ *
292
+ * @param string $class_name The classname to set.
293
+ *
294
+ * @return void
295
+ */
296
+ public function set_class_name( $class_name ) {
297
+ $this->class_name = $class_name;
298
+ }
299
+
300
+ /**
301
+ * Create a new, empty instance of the class. Used
302
+ * to add a new row to your database. May optionally
303
+ * be passed an associative array of data to populate
304
+ * the instance. If so, all fields will be flagged as
305
+ * dirty so all will be saved to the database when
306
+ * save() is called.
307
+ *
308
+ * @param array|null $data Data to populate table.
309
+ *
310
+ * @return bool|Model|ORM
311
+ */
312
+ public function create( $data = null ) {
313
+ $this->_is_new = true;
314
+ if ( ! \is_null( $data ) ) {
315
+ $this->hydrate( $data )->force_all_dirty();
316
+ }
317
+
318
+ return $this->create_model_instance( $this );
319
+ }
320
+
321
+ /**
322
+ * Specify the ID column to use for this instance or array of instances only.
323
+ * This overrides the id_column and id_column_overrides settings.
324
+ *
325
+ * This is mostly useful for libraries built on top of Idiorm, and will
326
+ * not normally be used in manually built queries. If you don't know why
327
+ * you would want to use this, you should probably just ignore it.
328
+ *
329
+ * @param string $id_column The ID column.
330
+ *
331
+ * @return ORM
332
+ */
333
+ public function use_id_column( $id_column ) {
334
+ $this->_instance_id_column = $id_column;
335
+
336
+ return $this;
337
+ }
338
+
339
+ /**
340
+ * Create an ORM instance from the given row (an associative
341
+ * array of data fetched from the database)
342
+ *
343
+ * @param array $row A row from the database.
344
+ *
345
+ * @return bool|Model
346
+ */
347
+ protected function _create_instance_from_row( $row ) {
348
+ $instance = self::for_table( $this->_table_name );
349
+ $instance->use_id_column( $this->_instance_id_column );
350
+ $instance->hydrate( $row );
351
+
352
+ return $this->create_model_instance( $instance );
353
+ }
354
+
355
+ /**
356
+ * Tell the ORM that you are expecting a single result
357
+ * back from your query, and execute it. Will return
358
+ * a single instance of the ORM class, or false if no
359
+ * rows were returned.
360
+ * As a shortcut, you may supply an ID as a parameter
361
+ * to this method. This will perform a primary key
362
+ * lookup on the table.
363
+ *
364
+ * @param null|int $id An (optional) ID.
365
+ *
366
+ * @return bool|Model
367
+ */
368
+ public function find_one( $id = null ) {
369
+ if ( ! \is_null( $id ) ) {
370
+ $this->where_id_is( $id );
371
+ }
372
+ $this->limit( 1 );
373
+ $rows = $this->_run();
374
+ if ( empty( $rows ) ) {
375
+ return false;
376
+ }
377
+
378
+ return $this->_create_instance_from_row( $rows[0] );
379
+ }
380
+
381
+ /**
382
+ * Tell the ORM that you are expecting multiple results
383
+ * from your query, and execute it. Will return an array
384
+ * of instances of the ORM class, or an empty array if
385
+ * no rows were returned.
386
+ *
387
+ * @return array
388
+ */
389
+ public function find_many() {
390
+ $rows = $this->_run();
391
+
392
+ if ( $rows === false ) {
393
+ return [];
394
+ }
395
+
396
+ return \array_map( [ $this, '_create_instance_from_row' ], $rows );
397
+ }
398
+
399
+ /**
400
+ * Method to create an instance of the model class associated with this
401
+ * wrapper and populate it with the supplied Idiorm instance.
402
+ *
403
+ * @param ORM $orm The ORM used by model.
404
+ *
405
+ * @return bool|Model Instance of the model class.
406
+ */
407
+ protected function create_model_instance( $orm ) {
408
+ if ( $orm === false ) {
409
+ return false;
410
+ }
411
+
412
+ /**
413
+ * An instance of Model is being made.
414
+ *
415
+ * @var Model $model
416
+ */
417
+ $model = new $this->class_name();
418
+ $model->set_orm( $orm );
419
+
420
+ return $model;
421
+ }
422
+
423
+ /**
424
+ * Tell the ORM that you are expecting multiple results
425
+ * from your query, and execute it. Will return an array,
426
+ * or an empty array if no rows were returned.
427
+ *
428
+ * @return array The query results.
429
+ */
430
+ public function find_array() {
431
+ return $this->_run();
432
+ }
433
+
434
+ /**
435
+ * Tell the ORM that you wish to execute a COUNT query.
436
+ *
437
+ * @param string $column The table column.
438
+ *
439
+ * @return float|int An integer representing the number of rows returned.
440
+ */
441
+ public function count( $column = '*' ) {
442
+ return $this->_call_aggregate_db_function( __FUNCTION__, $column );
443
+ }
444
+
445
+ /**
446
+ * Tell the ORM that you wish to execute a MAX query.
447
+ *
448
+ * @param string $column The table column.
449
+ *
450
+ * @return float|int The max value of the chosen column.
451
+ */
452
+ public function max( $column ) {
453
+ return $this->_call_aggregate_db_function( __FUNCTION__, $column );
454
+ }
455
+
456
+ /**
457
+ * Tell the ORM that you wish to execute a MIN query.
458
+ *
459
+ * @param string $column The table column.
460
+ *
461
+ * @return float|int The min value of the chosen column.
462
+ */
463
+ public function min( $column ) {
464
+ return $this->_call_aggregate_db_function( __FUNCTION__, $column );
465
+ }
466
+
467
+ /**
468
+ * Tell the ORM that you wish to execute a AVG query.
469
+ *
470
+ * @param string $column The table column.
471
+ *
472
+ * @return float|int The average value of the chosen column.
473
+ */
474
+ public function avg( $column ) {
475
+ return $this->_call_aggregate_db_function( __FUNCTION__, $column );
476
+ }
477
+
478
+ /**
479
+ * Tell the ORM that you wish to execute a SUM query.
480
+ *
481
+ * @param string $column The table column.
482
+ *
483
+ * @return float|int The sum of the chosen column.
484
+ */
485
+ public function sum( $column ) {
486
+ return $this->_call_aggregate_db_function( __FUNCTION__, $column );
487
+ }
488
+
489
+ /**
490
+ * Returns the select query as SQL.
491
+ *
492
+ * @return string The select query in SQL.
493
+ */
494
+ public function get_sql() {
495
+ return $this->_build_select();
496
+ }
497
+
498
+ /**
499
+ * Returns the update query as SQL.
500
+ *
501
+ * @return string The update query in SQL.
502
+ */
503
+ public function get_update_sql() {
504
+ return $this->_build_update();
505
+ }
506
+
507
+ /**
508
+ * Execute an aggregate query on the current connection.
509
+ *
510
+ * @param string $sql_function The aggregate function to call eg. MIN, COUNT, etc
511
+ * @param string $column The column to execute the aggregate query against
512
+ *
513
+ * @return int
514
+ */
515
+ protected function _call_aggregate_db_function( $sql_function, $column ) {
516
+ $alias = \strtolower( $sql_function );
517
+ $sql_function = \strtoupper( $sql_function );
518
+ if ( '*' != $column ) {
519
+ $column = $this->_quote_identifier( $column );
520
+ }
521
+ $result_columns = $this->_result_columns;
522
+ $this->_result_columns = [];
523
+ $this->select_expr( "{$sql_function}({$column})", $alias );
524
+ $result = $this->find_one();
525
+ $this->_result_columns = $result_columns;
526
+ $return_value = 0;
527
+ if ( $result !== false && isset( $result->{$alias} ) ) {
528
+ if ( ! \is_numeric( $result->{$alias} ) ) {
529
+ $return_value = $result->{$alias};
530
+ } elseif ( (int) $result->{$alias} == (float) $result->{$alias} ) {
531
+ $return_value = (int) $result->{$alias};
532
+ } else {
533
+ $return_value = (float) $result->{$alias};
534
+ }
535
+ }
536
+
537
+ return $return_value;
538
+ }
539
+
540
+ /**
541
+ * This method can be called to hydrate (populate) this
542
+ * instance of the class from an associative array of data.
543
+ * This will usually be called only from inside the class,
544
+ * but it's public in case you need to call it directly.
545
+ *
546
+ * @param array $data Data to populate table.
547
+ *
548
+ * @return ORM
549
+ */
550
+ public function hydrate( $data = [] ) {
551
+ $this->_data = $data;
552
+
553
+ return $this;
554
+ }
555
+
556
+ /**
557
+ * Force the ORM to flag all the fields in the $data array
558
+ * as "dirty" and therefore update them when save() is called.
559
+ *
560
+ * @return ORM
561
+ */
562
+ public function force_all_dirty() {
563
+ $this->_dirty_fields = $this->_data;
564
+
565
+ return $this;
566
+ }
567
+
568
+ /**
569
+ * Perform a raw query. The query can contain placeholders in
570
+ * either named or question mark style. If placeholders are
571
+ * used, the parameters should be an array of values which will
572
+ * be bound to the placeholders in the query. If this method
573
+ * is called, all other query building methods will be ignored.
574
+ *
575
+ * @param array $query The query.
576
+ * @param array $parameters
577
+ *
578
+ * @return ORM
579
+ */
580
+ public function raw_query( $query, $parameters = [] ) {
581
+ $this->_is_raw_query = true;
582
+ $this->_raw_query = $query;
583
+ $this->_raw_parameters = $parameters;
584
+
585
+ return $this;
586
+ }
587
+
588
+ /**
589
+ * Add an alias for the main table to be used in SELECT queries
590
+ *
591
+ * @param string $alias
592
+ *
593
+ * @return ORM
594
+ */
595
+ public function table_alias( $alias ) {
596
+ $this->_table_alias = $alias;
597
+
598
+ return $this;
599
+ }
600
+
601
+ /**
602
+ * Internal method to add an unquoted expression to the set
603
+ * of columns returned by the SELECT query. The second optional
604
+ * argument is the alias to return the expression as.
605
+ *
606
+ * @param string $expr
607
+ * @param null|string $alias
608
+ *
609
+ * @return ORM
610
+ */
611
+ protected function _add_result_column( $expr, $alias = null ) {
612
+ if ( ! \is_null( $alias ) ) {
613
+ $expr .= ' AS ' . $this->_quote_identifier( $alias );
614
+ }
615
+ if ( $this->_using_default_result_columns ) {
616
+ $this->_result_columns = [ $expr ];
617
+ $this->_using_default_result_columns = false;
618
+ } else {
619
+ $this->_result_columns[] = $expr;
620
+ }
621
+
622
+ return $this;
623
+ }
624
+
625
+ /**
626
+ * Counts the number of columns that belong to the primary
627
+ * key and their value is null.
628
+ *
629
+ * @return int
630
+ *
631
+ * @throws \Exception
632
+ */
633
+ public function count_null_id_columns() {
634
+ if ( \is_array( $this->_get_id_column_name() ) ) {
635
+ return \count( \array_filter( $this->id(), 'is_null' ) );
636
+ } else {
637
+ return \is_null( $this->id() ) ? 1 : 0;
638
+ }
639
+ }
640
+
641
+ /**
642
+ * Add a column to the list of columns returned by the SELECT
643
+ * query. This defaults to '*'. The second optional argument is
644
+ * the alias to return the column as.
645
+ *
646
+ * @param string $column
647
+ * @param null|string $alias
648
+ *
649
+ * @return ORM
650
+ */
651
+ public function select( $column, $alias = null ) {
652
+ $column = $this->_quote_identifier( $column );
653
+
654
+ return $this->_add_result_column( $column, $alias );
655
+ }
656
+
657
+ /**
658
+ * Add an unquoted expression to the list of columns returned
659
+ * by the SELECT query. The second optional argument is
660
+ * the alias to return the column as.
661
+ *
662
+ * @param string $expr
663
+ * @param null|string $alias
664
+ *
665
+ * @return ORM
666
+ */
667
+ public function select_expr( $expr, $alias = null ) {
668
+ return $this->_add_result_column( $expr, $alias );
669
+ }
670
+
671
+ /**
672
+ * Add columns to the list of columns returned by the SELECT
673
+ * query. This defaults to '*'. Many columns can be supplied
674
+ * as either an array or as a list of parameters to the method.
675
+ *
676
+ * Note that the alias must not be numeric - if you want a
677
+ * numeric alias then prepend it with some alpha chars. eg. a1
678
+ *
679
+ * @example select_many(array('column', 'column2', 'column3'), 'column4', 'column5');
680
+ * @example select_many(array('alias' => 'column', 'column2', 'alias2' => 'column3'), 'column4', 'column5');
681
+ * @example select_many('column', 'column2', 'column3');
682
+ *
683
+ * @return ORM
684
+ */
685
+ public function select_many() {
686
+ $columns = \func_get_args();
687
+ if ( ! empty( $columns ) ) {
688
+ $columns = $this->_normalise_select_many_columns( $columns );
689
+ foreach ( $columns as $alias => $column ) {
690
+ if ( \is_numeric( $alias ) ) {
691
+ $alias = null;
692
+ }
693
+ $this->select( $column, $alias );
694
+ }
695
+ }
696
+
697
+ return $this;
698
+ }
699
+
700
+ /**
701
+ * Add an unquoted expression to the list of columns returned
702
+ * by the SELECT query. Many columns can be supplied as either
703
+ * an array or as a list of parameters to the method.
704
+ *
705
+ * Note that the alias must not be numeric - if you want a
706
+ * numeric alias then prepend it with some alpha chars. eg. a1
707
+ *
708
+ * @return ORM
709
+ * @example select_many_expr(array('column', 'column2', 'column3'), 'column4', 'column5')
710
+ * @example select_many_expr(array('alias' => 'column', 'column2', 'alias2' => 'column3'), 'column4', 'column5')
711
+ *
712
+ * @example select_many_expr('column', 'column2', 'column3')
713
+ */
714
+ public function select_many_expr() {
715
+ $columns = \func_get_args();
716
+ if ( ! empty( $columns ) ) {
717
+ $columns = $this->_normalise_select_many_columns( $columns );
718
+ foreach ( $columns as $alias => $column ) {
719
+ if ( \is_numeric( $alias ) ) {
720
+ $alias = null;
721
+ }
722
+ $this->select_expr( $column, $alias );
723
+ }
724
+ }
725
+
726
+ return $this;
727
+ }
728
+
729
+ /**
730
+ * Take a column specification for the select many methods and convert it
731
+ * into a normalised array of columns and aliases.
732
+ *
733
+ * It is designed to turn the following styles into a normalised array:
734
+ *
735
+ * array(array('alias' => 'column', 'column2', 'alias2' => 'column3'), 'column4', 'column5'))
736
+ *
737
+ * @param array $columns
738
+ *
739
+ * @return array
740
+ */
741
+ protected function _normalise_select_many_columns( $columns ) {
742
+ $return = [];
743
+ foreach ( $columns as $column ) {
744
+ if ( \is_array( $column ) ) {
745
+ foreach ( $column as $key => $value ) {
746
+ if ( ! \is_numeric( $key ) ) {
747
+ $return[ $key ] = $value;
748
+ } else {
749
+ $return[] = $value;
750
+ }
751
+ }
752
+ } else {
753
+ $return[] = $column;
754
+ }
755
+ }
756
+
757
+ return $return;
758
+ }
759
+
760
+ /**
761
+ * Add a DISTINCT keyword before the list of columns in the SELECT query
762
+ *
763
+ * @return ORM
764
+ */
765
+ public function distinct() {
766
+ $this->_distinct = true;
767
+
768
+ return $this;
769
+ }
770
+
771
+ /**
772
+ * Internal method to add a JOIN source to the query.
773
+ *
774
+ * The join_operator should be one of INNER, LEFT OUTER, CROSS etc - this
775
+ * will be prepended to JOIN.
776
+ *
777
+ * The table should be the name of the table to join to.
778
+ *
779
+ * The constraint may be either a string or an array with three elements. If it
780
+ * is a string, it will be compiled into the query as-is, with no escaping. The
781
+ * recommended way to supply the constraint is as an array with three elements:
782
+ *
783
+ * first_column, operator, second_column
784
+ *
785
+ * Example: array('user.id', '=', 'profile.user_id')
786
+ *
787
+ * will compile to
788
+ *
789
+ * ON `user`.`id` = `profile`.`user_id`
790
+ *
791
+ * The final (optional) argument specifies an alias for the joined table.
792
+ *
793
+ * @param string $join_operator
794
+ * @param string $table
795
+ * @param string $constraint
796
+ * @param null $table_alias
797
+ *
798
+ * @return ORM
799
+ */
800
+ protected function _add_join_source( $join_operator, $table, $constraint, $table_alias = null ) {
801
+ $join_operator = \trim( "{$join_operator} JOIN" );
802
+ $table = $this->_quote_identifier( $table );
803
+ // Add table alias if present
804
+ if ( ! \is_null( $table_alias ) ) {
805
+ $table_alias = $this->_quote_identifier( $table_alias );
806
+ $table .= " {$table_alias}";
807
+ }
808
+ // Build the constraint
809
+ if ( \is_array( $constraint ) ) {
810
+ list( $first_column, $operator, $second_column ) = $constraint;
811
+ $first_column = $this->_quote_identifier( $first_column );
812
+ $second_column = $this->_quote_identifier( $second_column );
813
+ $constraint = "{$first_column} {$operator} {$second_column}";
814
+ }
815
+ $this->_join_sources[] = "{$join_operator} {$table} ON {$constraint}";
816
+
817
+ return $this;
818
+ }
819
+
820
+ /**
821
+ * Add a RAW JOIN source to the query
822
+ *
823
+ * @param string $table
824
+ * @param string $constraint
825
+ * @param string $table_alias
826
+ * @param array $parameters
827
+ *
828
+ * @return ORM
829
+ */
830
+ public function raw_join( $table, $constraint, $table_alias, $parameters = [] ) {
831
+ // Add table alias if present
832
+ if ( ! \is_null( $table_alias ) ) {
833
+ $table_alias = $this->_quote_identifier( $table_alias );
834
+ $table .= " {$table_alias}";
835
+ }
836
+ $this->_values = \array_merge( $this->_values, $parameters );
837
+ // Build the constraint
838
+ if ( \is_array( $constraint ) ) {
839
+ list( $first_column, $operator, $second_column ) = $constraint;
840
+ $first_column = $this->_quote_identifier( $first_column );
841
+ $second_column = $this->_quote_identifier( $second_column );
842
+ $constraint = "{$first_column} {$operator} {$second_column}";
843
+ }
844
+ $this->_join_sources[] = "{$table} ON {$constraint}";
845
+
846
+ return $this;
847
+ }
848
+
849
+ /**
850
+ * Add a simple JOIN source to the query
851
+ *
852
+ * @param string $table
853
+ * @param string $constraint
854
+ * @param null $table_alias
855
+ *
856
+ * @return ORM
857
+ */
858
+ public function join( $table, $constraint, $table_alias = null ) {
859
+ return $this->_add_join_source( '', $table, $constraint, $table_alias );
860
+ }
861
+
862
+ /**
863
+ * Add an INNER JOIN souce to the query
864
+ *
865
+ * @param string $table
866
+ * @param string $constraint
867
+ * @param null $table_alias
868
+ *
869
+ * @return ORM
870
+ */
871
+ public function inner_join( $table, $constraint, $table_alias = null ) {
872
+ return $this->_add_join_source( 'INNER', $table, $constraint, $table_alias );
873
+ }
874
+
875
+ /**
876
+ * Add a LEFT OUTER JOIN souce to the query
877
+ *
878
+ * @param string $table
879
+ * @param string $constraint
880
+ * @param null $table_alias
881
+ *
882
+ * @return ORM
883
+ */
884
+ public function left_outer_join( $table, $constraint, $table_alias = null ) {
885
+ return $this->_add_join_source( 'LEFT OUTER', $table, $constraint, $table_alias );
886
+ }
887
+
888
+ /**
889
+ * Add an RIGHT OUTER JOIN souce to the query
890
+ *
891
+ * @param string $table
892
+ * @param string $constraint
893
+ * @param null $table_alias
894
+ *
895
+ * @return ORM
896
+ */
897
+ public function right_outer_join( $table, $constraint, $table_alias = null ) {
898
+ return $this->_add_join_source( 'RIGHT OUTER', $table, $constraint, $table_alias );
899
+ }
900
+
901
+ /**
902
+ * Add an FULL OUTER JOIN souce to the query
903
+ *
904
+ * @param string $table
905
+ * @param string $constraint
906
+ * @param null $table_alias
907
+ *
908
+ * @return ORM
909
+ */
910
+ public function full_outer_join( $table, $constraint, $table_alias = null ) {
911
+ return $this->_add_join_source( 'FULL OUTER', $table, $constraint, $table_alias );
912
+ }
913
+
914
+ /**
915
+ * Internal method to add a HAVING condition to the query
916
+ *
917
+ * @param string $fragment
918
+ * @param array $values
919
+ *
920
+ * @return ORM
921
+ */
922
+ protected function _add_having( $fragment, $values = [] ) {
923
+ return $this->_add_condition( 'having', $fragment, $values );
924
+ }
925
+
926
+ /**
927
+ * Internal method to add a HAVING condition to the query
928
+ *
929
+ * @param string $column_name The table column.
930
+ * @param string $separator
931
+ * @param $value
932
+ *
933
+ * @return ORM
934
+ */
935
+ protected function _add_simple_having( $column_name, $separator, $value ) {
936
+ return $this->_add_simple_condition( 'having', $column_name, $separator, $value );
937
+ }
938
+
939
+ /**
940
+ * Internal method to add a HAVING clause with multiple values (like IN and NOT IN)
941
+ *
942
+ * @param string|array $column_name The table column.
943
+ * @param string $separator
944
+ * @param $values
945
+ *
946
+ * @return ORM
947
+ */
948
+ public function _add_having_placeholder( $column_name, $separator, $values ) {
949
+ if ( ! \is_array( $column_name ) ) {
950
+ $data = [ $column_name => $values ];
951
+ } else {
952
+ $data = $column_name;
953
+ }
954
+ $result = $this;
955
+ foreach ( $data as $key => $val ) {
956
+ $column = $result->_quote_identifier( $key );
957
+ $placeholders = $result->_create_placeholders( $val );
958
+ $result = $result->_add_having( "{$column} {$separator} ({$placeholders})", $val );
959
+ }
960
+
961
+ return $result;
962
+ }
963
+
964
+ /**
965
+ * Internal method to add a HAVING clause with no parameters(like IS NULL and IS NOT NULL)
966
+ *
967
+ * @param string $column_name
968
+ * @param $operator
969
+ *
970
+ * @return ORM
971
+ */
972
+ public function _add_having_no_value( $column_name, $operator ) {
973
+ $conditions = \is_array( $column_name ) ? $column_name : [ $column_name ];
974
+ $result = $this;
975
+ foreach ( $conditions as $column ) {
976
+ $column = $this->_quote_identifier( $column );
977
+ $result = $result->_add_having( "{$column} {$operator}" );
978
+ }
979
+
980
+ return $result;
981
+ }
982
+
983
+ /**
984
+ * Internal method to add a WHERE condition to the query
985
+ *
986
+ * @param string $fragment
987
+ * @param array $values
988
+ *
989
+ * @return ORM
990
+ */
991
+ protected function _add_where( $fragment, $values = [] ) {
992
+ return $this->_add_condition( 'where', $fragment, $values );
993
+ }
994
+
995
+ /**
996
+ * Internal method to add a WHERE condition to the query
997
+ *
998
+ * @param string|array $column_name The table column
999
+ * @param string $separator
1000
+ * @param $value
1001
+ *
1002
+ * @return ORM
1003
+ */
1004
+ protected function _add_simple_where( $column_name, $separator, $value ) {
1005
+ return $this->_add_simple_condition( 'where', $column_name, $separator, $value );
1006
+ }
1007
+
1008
+ /**
1009
+ * Add a WHERE clause with multiple values (like IN and NOT IN)
1010
+ *
1011
+ * @param string|array $column_name The table column
1012
+ * @param string $separator
1013
+ * @param $values
1014
+ *
1015
+ * @return ORM
1016
+ */
1017
+ public function _add_where_placeholder( $column_name, $separator, $values ) {
1018
+ if ( ! \is_array( $column_name ) ) {
1019
+ $data = [ $column_name => $values ];
1020
+ } else {
1021
+ $data = $column_name;
1022
+ }
1023
+ $result = $this;
1024
+ foreach ( $data as $key => $val ) {
1025
+ $column = $result->_quote_identifier( $key );
1026
+ $placeholders = $result->_create_placeholders( $val );
1027
+ $result = $result->_add_where( "{$column} {$separator} ({$placeholders})", $val );
1028
+ }
1029
+
1030
+ return $result;
1031
+ }
1032
+
1033
+ /**
1034
+ * Add a WHERE clause with no parameters(like IS NULL and IS NOT NULL)
1035
+ *
1036
+ * @param string $column_name
1037
+ * @param $operator
1038
+ *
1039
+ * @return ORM
1040
+ */
1041
+ public function _add_where_no_value( $column_name, $operator ) {
1042
+ $conditions = \is_array( $column_name ) ? $column_name : [ $column_name ];
1043
+ $result = $this;
1044
+ foreach ( $conditions as $column ) {
1045
+ $column = $this->_quote_identifier( $column );
1046
+ $result = $result->_add_where( "{$column} {$operator}" );
1047
+ }
1048
+
1049
+ return $result;
1050
+ }
1051
+
1052
+ /**
1053
+ * Internal method to add a HAVING or WHERE condition to the query
1054
+ *
1055
+ * @param $type
1056
+ * @param string $fragment
1057
+ * @param array $values
1058
+ *
1059
+ * @return ORM
1060
+ */
1061
+ protected function _add_condition( $type, $fragment, $values = [] ) {
1062
+ $conditions_class_property_name = "_{$type}_conditions";
1063
+ if ( ! \is_array( $values ) ) {
1064
+ $values = [ $values ];
1065
+ }
1066
+ \array_push( $this->{$conditions_class_property_name}, [
1067
+ self::CONDITION_FRAGMENT => $fragment,
1068
+ self::CONDITION_VALUES => $values,
1069
+ ] );
1070
+
1071
+ return $this;
1072
+ }
1073
+
1074
+ /**
1075
+ * Helper method to compile a simple COLUMN SEPARATOR VALUE
1076
+ * style HAVING or WHERE condition into a string and value ready to
1077
+ * be passed to the _add_condition method. Avoids duplication
1078
+ * of the call to _quote_identifier
1079
+ *
1080
+ * If column_name is an associative array, it will add a condition for each column
1081
+ *
1082
+ * @param $type
1083
+ * @param string|array $column_name The table column
1084
+ * @param string $separator
1085
+ * @param $value
1086
+ *
1087
+ * @return ORM
1088
+ */
1089
+ protected function _add_simple_condition( $type, $column_name, $separator, $value ) {
1090
+ $multiple = \is_array( $column_name ) ? $column_name : [ $column_name => $value ];
1091
+ $result = $this;
1092
+ foreach ( $multiple as $key => $val ) {
1093
+ // Add the table name in case of ambiguous columns
1094
+ if ( \count( $result->_join_sources ) > 0 && \strpos( $key, '.' ) === false ) {
1095
+ $table = $result->_table_name;
1096
+ if ( ! \is_null( $result->_table_alias ) ) {
1097
+ $table = $result->_table_alias;
1098
+ }
1099
+ $key = "{$table}.{$key}";
1100
+ }
1101
+ $key = $result->_quote_identifier( $key );
1102
+ $placeholder = '%s';
1103
+ $result = $result->_add_condition( $type, "{$key} {$separator} {$placeholder}", $val );
1104
+ }
1105
+
1106
+ return $result;
1107
+ }
1108
+
1109
+ /**
1110
+ * Return a string containing the given number of question marks,
1111
+ * separated by commas. Eg "?, ?, ?"
1112
+ *
1113
+ * @param array $fields Fields to create placeholder for.
1114
+ *
1115
+ * @return string
1116
+ */
1117
+ protected function _create_placeholders( $fields ) {
1118
+ if ( ! empty( $fields ) ) {
1119
+ $db_fields = [];
1120
+ foreach ( $fields as $key => $value ) {
1121
+ // Process expression fields directly into the query
1122
+ if ( \array_key_exists( $key, $this->_expr_fields ) ) {
1123
+ $db_fields[] = $value;
1124
+ } else {
1125
+ $db_fields[] = '%s';
1126
+ }
1127
+ }
1128
+
1129
+ return \implode( ', ', $db_fields );
1130
+ }
1131
+
1132
+ return '';
1133
+ }
1134
+
1135
+ /**
1136
+ * Helper method that filters a column/value array returning only those
1137
+ * columns that belong to a compound primary key.
1138
+ *
1139
+ * If the key contains a column that does not exist in the given array,
1140
+ * a null value will be returned for it.
1141
+ *
1142
+ * @param $value
1143
+ *
1144
+ * @return array
1145
+ */
1146
+ protected function _get_compound_id_column_values( $value ) {
1147
+ $filtered = [];
1148
+ foreach ( $this->_get_id_column_name() as $key ) {
1149
+ $filtered[ $key ] = isset( $value[ $key ] ) ? $value[ $key ] : null;
1150
+ }
1151
+
1152
+ return $filtered;
1153
+ }
1154
+
1155
+ /**
1156
+ * Helper method that filters an array containing compound column/value
1157
+ * arrays.
1158
+ *
1159
+ * @param $values
1160
+ *
1161
+ * @return array
1162
+ */
1163
+ protected function _get_compound_id_column_values_array( $values ) {
1164
+ $filtered = [];
1165
+ foreach ( $values as $value ) {
1166
+ $filtered[] = $this->_get_compound_id_column_values( $value );
1167
+ }
1168
+
1169
+ return $filtered;
1170
+ }
1171
+
1172
+ /**
1173
+ * Add a WHERE column = value clause to your query. Each time
1174
+ * this is called in the chain, an additional WHERE will be
1175
+ * added, and these will be ANDed together when the final query
1176
+ * is built.
1177
+ *
1178
+ * If you use an array in $column_name, a new clause will be
1179
+ * added for each element. In this case, $value is ignored.
1180
+ *
1181
+ * @param string|array $column_name The table column
1182
+ * @param null $value
1183
+ *
1184
+ * @return ORM
1185
+ */
1186
+ public function where( $column_name, $value = null ) {
1187
+ return $this->where_equal( $column_name, $value );
1188
+ }
1189
+
1190
+ /**
1191
+ * More explicitly named version of for the where() method.
1192
+ * Can be used if preferred.
1193
+ *
1194
+ * @param string|array $column_name The table column
1195
+ * @param null $value
1196
+ *
1197
+ * @return ORM
1198
+ */
1199
+ public function where_equal( $column_name, $value = null ) {
1200
+ return $this->_add_simple_where( $column_name, '=', $value );
1201
+ }
1202
+
1203
+ /**
1204
+ * Add a WHERE column != value clause to your query.
1205
+ *
1206
+ * @param string|array $column_name The table column
1207
+ * @param null $value
1208
+ *
1209
+ * @return ORM
1210
+ */
1211
+ public function where_not_equal( $column_name, $value = null ) {
1212
+ return $this->_add_simple_where( $column_name, '!=', $value );
1213
+ }
1214
+
1215
+ /**
1216
+ * Special method to query the table by its primary key
1217
+ *
1218
+ * If primary key is compound, only the columns that
1219
+ * belong to they key will be used for the query
1220
+ *
1221
+ * @param $id
1222
+ *
1223
+ * @return ORM
1224
+ */
1225
+ public function where_id_is( $id ) {
1226
+ return \is_array( $this->_get_id_column_name() ) ? $this->where( $this->_get_compound_id_column_values( $id ), null ) : $this->where( $this->_get_id_column_name(), $id );
1227
+ }
1228
+
1229
+ /**
1230
+ * Allows adding a WHERE clause that matches any of the conditions
1231
+ * specified in the array. Each element in the associative array will
1232
+ * be a different condition, where the key will be the column name.
1233
+ *
1234
+ * By default, an equal operator will be used against all columns, but
1235
+ * it can be overriden for any or every column using the second parameter.
1236
+ *
1237
+ * Each condition will be ORed together when added to the final query.
1238
+ *
1239
+ * @param $values
1240
+ * @param string $operator
1241
+ *
1242
+ * @return ORM
1243
+ */
1244
+ public function where_any_is( $values, $operator = '=' ) {
1245
+ $data = [];
1246
+ $query = [ '((' ];
1247
+ $first = true;
1248
+ foreach ( $values as $value ) {
1249
+ if ( $first ) {
1250
+ $first = false;
1251
+ } else {
1252
+ $query[] = ') OR (';
1253
+ }
1254
+ $firstsub = true;
1255
+ foreach ( $value as $key => $item ) {
1256
+ $op = \is_string( $operator ) ? $operator : ( isset( $operator[ $key ] ) ? $operator[ $key ] : '=' );
1257
+ if ( $firstsub ) {
1258
+ $firstsub = false;
1259
+ } else {
1260
+ $query[] = 'AND';
1261
+ }
1262
+ $query[] = $this->_quote_identifier( $key );
1263
+ $data[] = $item;
1264
+ $query[] = $op . '%s';
1265
+ }
1266
+ }
1267
+ $query[] = '))';
1268
+
1269
+ return $this->where_raw( \join( $query, ' ' ), $data );
1270
+ }
1271
+
1272
+ /**
1273
+ * Similar to where_id_is() but allowing multiple primary keys.
1274
+ *
1275
+ * If primary key is compound, only the columns that
1276
+ * belong to they key will be used for the query
1277
+ *
1278
+ * @param $ids
1279
+ *
1280
+ * @return ORM
1281
+ */
1282
+ public function where_id_in( $ids ) {
1283
+ return \is_array( $this->_get_id_column_name() ) ? $this->where_any_is( $this->_get_compound_id_column_values_array( $ids ) ) : $this->where_in( $this->_get_id_column_name(), $ids );
1284
+ }
1285
+
1286
+ /**
1287
+ * Add a WHERE ... LIKE clause to your query.
1288
+ *
1289
+ * @param string|array $column_name The table column
1290
+ * @param null $value
1291
+ *
1292
+ * @return ORM
1293
+ */
1294
+ public function where_like( $column_name, $value = null ) {
1295
+ return $this->_add_simple_where( $column_name, 'LIKE', $value );
1296
+ }
1297
+
1298
+ /**
1299
+ * Add where WHERE ... NOT LIKE clause to your query.
1300
+ *
1301
+ * @param string|array $column_name The table column
1302
+ * @param null $value
1303
+ *
1304
+ * @return ORM
1305
+ */
1306
+ public function where_not_like( $column_name, $value = null ) {
1307
+ return $this->_add_simple_where( $column_name, 'NOT LIKE', $value );
1308
+ }
1309
+
1310
+ /**
1311
+ * Add a WHERE ... > clause to your query
1312
+ *
1313
+ * @param string|array $column_name The table column
1314
+ * @param null $value
1315
+ *
1316
+ * @return ORM
1317
+ */
1318
+ public function where_gt( $column_name, $value = null ) {
1319
+ return $this->_add_simple_where( $column_name, '>', $value );
1320
+ }
1321
+
1322
+ /**
1323
+ * Add a WHERE ... < clause to your query
1324
+ *
1325
+ * @param string|array $column_name The table column
1326
+ * @param null $value
1327
+ *
1328
+ * @return ORM
1329
+ */
1330
+ public function where_lt( $column_name, $value = null ) {
1331
+ return $this->_add_simple_where( $column_name, '<', $value );
1332
+ }
1333
+
1334
+ /**
1335
+ * Add a WHERE ... >= clause to your query
1336
+ *
1337
+ * @param string|array $column_name The table column
1338
+ * @param null $value
1339
+ *
1340
+ * @return ORM
1341
+ */
1342
+ public function where_gte( $column_name, $value = null ) {
1343
+ return $this->_add_simple_where( $column_name, '>=', $value );
1344
+ }
1345
+
1346
+ /**
1347
+ * Add a WHERE ... <= clause to your query
1348
+ *
1349
+ * @param string|array $column_name The table column
1350
+ * @param null $value
1351
+ *
1352
+ * @return ORM
1353
+ */
1354
+ public function where_lte( $column_name, $value = null ) {
1355
+ return $this->_add_simple_where( $column_name, '<=', $value );
1356
+ }
1357
+
1358
+ /**
1359
+ * Add a WHERE ... IN clause to your query
1360
+ *
1361
+ * @param string|array $column_name The table column
1362
+ * @param $values
1363
+ *
1364
+ * @return ORM
1365
+ */
1366
+ public function where_in( $column_name, $values ) {
1367
+ return $this->_add_where_placeholder( $column_name, 'IN', $values );
1368
+ }
1369
+
1370
+ /**
1371
+ * Add a WHERE ... NOT IN clause to your query
1372
+ *
1373
+ * @param string|array $column_name The table column
1374
+ * @param $values
1375
+ *
1376
+ * @return ORM
1377
+ */
1378
+ public function where_not_in( $column_name, $values ) {
1379
+ return $this->_add_where_placeholder( $column_name, 'NOT IN', $values );
1380
+ }
1381
+
1382
+ /**
1383
+ * Add a WHERE column IS NULL clause to your query
1384
+ *
1385
+ * @param string|array $column_name The table column
1386
+ *
1387
+ * @return ORM
1388
+ */
1389
+ public function where_null( $column_name ) {
1390
+ return $this->_add_where_no_value( $column_name, 'IS NULL' );
1391
+ }
1392
+
1393
+ /**
1394
+ * Add a WHERE column IS NOT NULL clause to your query
1395
+ *
1396
+ * @param string|array $column_name The table column
1397
+ *
1398
+ * @return ORM
1399
+ */
1400
+ public function where_not_null( $column_name ) {
1401
+ return $this->_add_where_no_value( $column_name, 'IS NOT NULL' );
1402
+ }
1403
+
1404
+ /**
1405
+ * Add a raw WHERE clause to the query. The clause should
1406
+ * contain question mark placeholders, which will be bound
1407
+ * to the parameters supplied in the second argument.
1408
+ *
1409
+ * @param $clause
1410
+ * @param array $parameters
1411
+ *
1412
+ * @return ORM
1413
+ */
1414
+ public function where_raw( $clause, $parameters = [] ) {
1415
+ return $this->_add_where( $clause, $parameters );
1416
+ }
1417
+
1418
+ /**
1419
+ * Add a LIMIT to the query
1420
+ *
1421
+ * @param $limit
1422
+ *
1423
+ * @return ORM
1424
+ */
1425
+ public function limit( $limit ) {
1426
+ $this->_limit = $limit;
1427
+
1428
+ return $this;
1429
+ }
1430
+
1431
+ /**
1432
+ * Add an OFFSET to the query
1433
+ *
1434
+ * @param $offset
1435
+ *
1436
+ * @return ORM
1437
+ */
1438
+ public function offset( $offset ) {
1439
+ $this->_offset = $offset;
1440
+
1441
+ return $this;
1442
+ }
1443
+
1444
+ /**
1445
+ * Add an ORDER BY clause to the query
1446
+ *
1447
+ * @param string $column_name
1448
+ * @param $ordering
1449
+ *
1450
+ * @return ORM
1451
+ */
1452
+ protected function _add_order_by( $column_name, $ordering ) {
1453
+ $column_name = $this->_quote_identifier( $column_name );
1454
+ $this->_order_by[] = "{$column_name} {$ordering}";
1455
+
1456
+ return $this;
1457
+ }
1458
+
1459
+ /**
1460
+ * Add an ORDER BY column DESC clause
1461
+ *
1462
+ * @param string|array $column_name The table column
1463
+ *
1464
+ * @return ORM
1465
+ */
1466
+ public function order_by_desc( $column_name ) {
1467
+ return $this->_add_order_by( $column_name, 'DESC' );
1468
+ }
1469
+
1470
+ /**
1471
+ * Add an ORDER BY column ASC clause
1472
+ *
1473
+ * @param string|array $column_name The table column
1474
+ *
1475
+ * @return ORM
1476
+ */
1477
+ public function order_by_asc( $column_name ) {
1478
+ return $this->_add_order_by( $column_name, 'ASC' );
1479
+ }
1480
+
1481
+ /**
1482
+ * Add an unquoted expression as an ORDER BY clause
1483
+ *
1484
+ * @param $clause
1485
+ *
1486
+ * @return ORM
1487
+ */
1488
+ public function order_by_expr( $clause ) {
1489
+ $this->_order_by[] = $clause;
1490
+
1491
+ return $this;
1492
+ }
1493
+
1494
+ /**
1495
+ * Add a column to the list of columns to GROUP BY
1496
+ *
1497
+ * @param string|array $column_name The table column
1498
+ *
1499
+ * @return ORM
1500
+ */
1501
+ public function group_by( $column_name ) {
1502
+ $column_name = $this->_quote_identifier( $column_name );
1503
+ $this->_group_by[] = $column_name;
1504
+
1505
+ return $this;
1506
+ }
1507
+
1508
+ /**
1509
+ * Add an unquoted expression to the list of columns to GROUP BY
1510
+ *
1511
+ * @param string $expr
1512
+ *
1513
+ * @return ORM
1514
+ */
1515
+ public function group_by_expr( $expr ) {
1516
+ $this->_group_by[] = $expr;
1517
+
1518
+ return $this;
1519
+ }
1520
+
1521
+ /**
1522
+ * Add a HAVING column = value clause to your query. Each time
1523
+ * this is called in the chain, an additional HAVING will be
1524
+ * added, and these will be ANDed together when the final query
1525
+ * is built.
1526
+ *
1527
+ * If you use an array in $column_name, a new clause will be
1528
+ * added for each element. In this case, $value is ignored.
1529
+ *
1530
+ * @param string|array $column_name The table column
1531
+ * @param null $value
1532
+ *
1533
+ * @return ORM
1534
+ */
1535
+ public function having( $column_name, $value = null ) {
1536
+ return $this->having_equal( $column_name, $value );
1537
+ }
1538
+
1539
+ /**
1540
+ * More explicitly named version of for the having() method.
1541
+ * Can be used if preferred.
1542
+ *
1543
+ * @param string|array $column_name The table column
1544
+ * @param null $value
1545
+ *
1546
+ * @return ORM
1547
+ */
1548
+ public function having_equal( $column_name, $value = null ) {
1549
+ return $this->_add_simple_having( $column_name, '=', $value );
1550
+ }
1551
+
1552
+ /**
1553
+ * Add a HAVING column != value clause to your query.
1554
+ *
1555
+ * @param string|array $column_name The table column
1556
+ * @param null $value
1557
+ *
1558
+ * @return ORM
1559
+ */
1560
+ public function having_not_equal( $column_name, $value = null ) {
1561
+ return $this->_add_simple_having( $column_name, '!=', $value );
1562
+ }
1563
+
1564
+ /**
1565
+ * Special method to query the table by its primary key.
1566
+ *
1567
+ * If primary key is compound, only the columns that
1568
+ * belong to they key will be used for the query
1569
+ *
1570
+ * @param $id
1571
+ *
1572
+ * @return ORM
1573
+ */
1574
+ public function having_id_is( $id ) {
1575
+ return \is_array( $this->_get_id_column_name() ) ? $this->having( $this->_get_compound_id_column_values( $id ), null ) : $this->having( $this->_get_id_column_name(), $id );
1576
+ }
1577
+
1578
+ /**
1579
+ * Add a HAVING ... LIKE clause to your query.
1580
+ *
1581
+ * @param string|array $column_name The table column
1582
+ * @param null $value
1583
+ *
1584
+ * @return ORM
1585
+ */
1586
+ public function having_like( $column_name, $value = null ) {
1587
+ return $this->_add_simple_having( $column_name, 'LIKE', $value );
1588
+ }
1589
+
1590
+ /**
1591
+ * Add where HAVING ... NOT LIKE clause to your query.
1592
+ *
1593
+ * @param string|array $column_name The table column
1594
+ * @param null $value
1595
+ *
1596
+ * @return ORM
1597
+ */
1598
+ public function having_not_like( $column_name, $value = null ) {
1599
+ return $this->_add_simple_having( $column_name, 'NOT LIKE', $value );
1600
+ }
1601
+
1602
+ /**
1603
+ * Add a HAVING ... > clause to your query
1604
+ *
1605
+ * @param string|array $column_name The table column
1606
+ * @param null $value
1607
+ *
1608
+ * @return ORM
1609
+ */
1610
+ public function having_gt( $column_name, $value = null ) {
1611
+ return $this->_add_simple_having( $column_name, '>', $value );
1612
+ }
1613
+
1614
+ /**
1615
+ * Add a HAVING ... < clause to your query
1616
+ *
1617
+ * @param string|array $column_name The table column
1618
+ * @param null $value
1619
+ *
1620
+ * @return ORM
1621
+ */
1622
+ public function having_lt( $column_name, $value = null ) {
1623
+ return $this->_add_simple_having( $column_name, '<', $value );
1624
+ }
1625
+
1626
+ /**
1627
+ * Add a HAVING ... >= clause to your query
1628
+ *
1629
+ * @param string|array $column_name The table column
1630
+ * @param null $value
1631
+ *
1632
+ * @return ORM
1633
+ */
1634
+ public function having_gte( $column_name, $value = null ) {
1635
+ return $this->_add_simple_having( $column_name, '>=', $value );
1636
+ }
1637
+
1638
+ /**
1639
+ * Add a HAVING ... <= clause to your query
1640
+ *
1641
+ * @param string|array $column_name The table column
1642
+ * @param null $value
1643
+ *
1644
+ * @return ORM
1645
+ */
1646
+ public function having_lte( $column_name, $value = null ) {
1647
+ return $this->_add_simple_having( $column_name, '<=', $value );
1648
+ }
1649
+
1650
+ /**
1651
+ * Add a HAVING ... IN clause to your query
1652
+ *
1653
+ * @param string|array $column_name The table column
1654
+ * @param null $values
1655
+ *
1656
+ * @return ORM
1657
+ */
1658
+ public function having_in( $column_name, $values = null ) {
1659
+ return $this->_add_having_placeholder( $column_name, 'IN', $values );
1660
+ }
1661
+
1662
+ /**
1663
+ * Add a HAVING ... NOT IN clause to your query
1664
+ *
1665
+ * @param string|array $column_name The table column
1666
+ * @param null $values
1667
+ *
1668
+ * @return ORM
1669
+ */
1670
+ public function having_not_in( $column_name, $values = null ) {
1671
+ return $this->_add_having_placeholder( $column_name, 'NOT IN', $values );
1672
+ }
1673
+
1674
+ /**
1675
+ * Add a HAVING column IS NULL clause to your query
1676
+ *
1677
+ * @param string|array $column_name The table column
1678
+ *
1679
+ * @return ORM
1680
+ */
1681
+ public function having_null( $column_name ) {
1682
+ return $this->_add_having_no_value( $column_name, 'IS NULL' );
1683
+ }
1684
+
1685
+ /**
1686
+ * Add a HAVING column IS NOT NULL clause to your query
1687
+ *
1688
+ * @param string|array $column_name The table column
1689
+ *
1690
+ * @return ORM
1691
+ */
1692
+ public function having_not_null( $column_name ) {
1693
+ return $this->_add_having_no_value( $column_name, 'IS NOT NULL' );
1694
+ }
1695
+
1696
+ /**
1697
+ * Add a raw HAVING clause to the query. The clause should
1698
+ * contain question mark placeholders, which will be bound
1699
+ * to the parameters supplied in the second argument.
1700
+ *
1701
+ * @param $clause
1702
+ * @param array $parameters
1703
+ *
1704
+ * @return ORM
1705
+ */
1706
+ public function having_raw( $clause, $parameters = [] ) {
1707
+ return $this->_add_having( $clause, $parameters );
1708
+ }
1709
+
1710
+ /**
1711
+ * Build a SELECT statement based on the clauses that have
1712
+ * been passed to this instance by chaining method calls.
1713
+ *
1714
+ * @return string
1715
+ */
1716
+ protected function _build_select() {
1717
+ // If the query is raw, just set the $this->_values to be
1718
+ // the raw query parameters and return the raw query
1719
+ if ( $this->_is_raw_query ) {
1720
+ $this->_values = $this->_raw_parameters;
1721
+
1722
+ return $this->_raw_query;
1723
+ }
1724
+ // Build and return the full SELECT statement by concatenating
1725
+ // the results of calling each separate builder method.
1726
+ return $this->_join_if_not_empty( ' ', [
1727
+ $this->_build_select_start(),
1728
+ $this->_build_join(),
1729
+ $this->_build_where(),
1730
+ $this->_build_group_by(),
1731
+ $this->_build_having(),
1732
+ $this->_build_order_by(),
1733
+ $this->_build_limit(),
1734
+ $this->_build_offset(),
1735
+ ] );
1736
+ }
1737
+
1738
+ /**
1739
+ * Build the start of the SELECT statement
1740
+ *
1741
+ * @return string
1742
+ */
1743
+ protected function _build_select_start() {
1744
+ $fragment = 'SELECT ';
1745
+ $result_columns = \join( ', ', $this->_result_columns );
1746
+ if ( $this->_distinct ) {
1747
+ $result_columns = 'DISTINCT ' . $result_columns;
1748
+ }
1749
+ $fragment .= "{$result_columns} FROM " . $this->_quote_identifier( $this->_table_name );
1750
+ if ( ! \is_null( $this->_table_alias ) ) {
1751
+ $fragment .= ' ' . $this->_quote_identifier( $this->_table_alias );
1752
+ }
1753
+
1754
+ return $fragment;
1755
+ }
1756
+
1757
+ /**
1758
+ * Build the JOIN sources
1759
+ *
1760
+ * @return string
1761
+ */
1762
+ protected function _build_join() {
1763
+ if ( \count( $this->_join_sources ) === 0 ) {
1764
+ return '';
1765
+ }
1766
+
1767
+ return \join( ' ', $this->_join_sources );
1768
+ }
1769
+
1770
+ /**
1771
+ * Build the WHERE clause(s)
1772
+ *
1773
+ * @return string
1774
+ */
1775
+ protected function _build_where() {
1776
+ return $this->_build_conditions( 'where' );
1777
+ }
1778
+
1779
+ /**
1780
+ * Build the HAVING clause(s)
1781
+ *
1782
+ * @return string
1783
+ */
1784
+ protected function _build_having() {
1785
+ return $this->_build_conditions( 'having' );
1786
+ }
1787
+
1788
+ /**
1789
+ * Build GROUP BY
1790
+ *
1791
+ * @return string
1792
+ */
1793
+ protected function _build_group_by() {
1794
+ if ( \count( $this->_group_by ) === 0 ) {
1795
+ return '';
1796
+ }
1797
+
1798
+ return 'GROUP BY ' . \join( ', ', $this->_group_by );
1799
+ }
1800
+
1801
+ /**
1802
+ * Build a WHERE or HAVING clause
1803
+ *
1804
+ * @param string $type
1805
+ *
1806
+ * @return string
1807
+ */
1808
+ protected function _build_conditions( $type ) {
1809
+ $conditions_class_property_name = "_{$type}_conditions";
1810
+ // If there are no clauses, return empty string
1811
+ if ( \count( $this->{$conditions_class_property_name} ) === 0 ) {
1812
+ return '';
1813
+ }
1814
+ $conditions = [];
1815
+ foreach ( $this->{$conditions_class_property_name} as $condition ) {
1816
+ $conditions[] = $condition[ self::CONDITION_FRAGMENT ];
1817
+ $this->_values = \array_merge( $this->_values, $condition[ self::CONDITION_VALUES ] );
1818
+ }
1819
+
1820
+ return \strtoupper( $type ) . ' ' . \join( ' AND ', $conditions );
1821
+ }
1822
+
1823
+ /**
1824
+ * Build ORDER BY
1825
+ */
1826
+ protected function _build_order_by() {
1827
+ if ( \count( $this->_order_by ) === 0 ) {
1828
+ return '';
1829
+ }
1830
+
1831
+ return 'ORDER BY ' . \join( ', ', $this->_order_by );
1832
+ }
1833
+
1834
+ /**
1835
+ * Build LIMIT
1836
+ */
1837
+ protected function _build_limit() {
1838
+ if ( ! \is_null( $this->_limit ) ) {
1839
+ return "LIMIT {$this->_limit}";
1840
+ }
1841
+
1842
+ return '';
1843
+ }
1844
+
1845
+ /**
1846
+ * Build OFFSET
1847
+ */
1848
+ protected function _build_offset() {
1849
+ if ( ! \is_null( $this->_offset ) ) {
1850
+ return 'OFFSET ' . $this->_offset;
1851
+ }
1852
+
1853
+ return '';
1854
+ }
1855
+
1856
+ /**
1857
+ * Wrapper around PHP's join function which
1858
+ * only adds the pieces if they are not empty.
1859
+ *
1860
+ * @param $glue
1861
+ * @param $pieces
1862
+ *
1863
+ * @return string
1864
+ */
1865
+ protected function _join_if_not_empty( $glue, $pieces ) {
1866
+ $filtered_pieces = [];
1867
+ foreach ( $pieces as $piece ) {
1868
+ if ( \is_string( $piece ) ) {
1869
+ $piece = \trim( $piece );
1870
+ }
1871
+ if ( ! empty( $piece ) ) {
1872
+ $filtered_pieces[] = $piece;
1873
+ }
1874
+ }
1875
+
1876
+ return \join( $glue, $filtered_pieces );
1877
+ }
1878
+
1879
+ /**
1880
+ * Quote a string that is used as an identifier
1881
+ * (table names, column names etc). This method can
1882
+ * also deal with dot-separated identifiers eg table.column
1883
+ *
1884
+ * @param $identifier
1885
+ *
1886
+ * @return string
1887
+ */
1888
+ protected function _quote_one_identifier( $identifier ) {
1889
+ $parts = \explode( '.', $identifier );
1890
+ $parts = \array_map( [ $this, '_quote_identifier_part' ], $parts );
1891
+
1892
+ return \join( '.', $parts );
1893
+ }
1894
+
1895
+ /**
1896
+ * Quote a string that is used as an identifier
1897
+ * (table names, column names etc) or an array containing
1898
+ * multiple identifiers. This method can also deal with
1899
+ * dot-separated identifiers eg table.column
1900
+ *
1901
+ * @param $identifier
1902
+ *
1903
+ * @return string
1904
+ */
1905
+ protected function _quote_identifier( $identifier ) {
1906
+ if ( \is_array( $identifier ) ) {
1907
+ $result = \array_map( [ $this, '_quote_one_identifier' ], $identifier );
1908
+
1909
+ return \join( ', ', $result );
1910
+ } else {
1911
+ return $this->_quote_one_identifier( $identifier );
1912
+ }
1913
+ }
1914
+
1915
+ /**
1916
+ * This method performs the actual quoting of a single
1917
+ * part of an identifier, using the identifier quote
1918
+ * character specified in the config (or autodetected).
1919
+ *
1920
+ * @param $part
1921
+ *
1922
+ * @return string
1923
+ */
1924
+ protected function _quote_identifier_part( $part ) {
1925
+ if ( $part === '*' ) {
1926
+ return $part;
1927
+ }
1928
+ $quote_character = '`';
1929
+
1930
+ // double up any identifier quotes to escape them
1931
+ return $quote_character . \str_replace( $quote_character, $quote_character . $quote_character, $part ) . $quote_character;
1932
+ }
1933
+
1934
+ /**
1935
+ * Execute the SELECT query that has been built up by chaining methods
1936
+ * on this class. Return an array of rows as associative arrays.
1937
+ *
1938
+ * @return array|false The result rows. False if the query failed.
1939
+ */
1940
+ protected function _run() {
1941
+ global $wpdb;
1942
+
1943
+ $query = $this->_build_select();
1944
+ $success = self::_execute( $query, $this->_values );
1945
+
1946
+ if ( $success === false ) {
1947
+ // If the query fails run the migrations and try again.
1948
+ // Action is intentionally undocumented and should not be used by third-parties.
1949
+ \do_action( '_yoast_run_migrations' );
1950
+ $success = self::_execute( $query, $this->_values );
1951
+ }
1952
+
1953
+ $this->_reset_idiorm_state();
1954
+
1955
+ if ( $success === false ) {
1956
+ return false;
1957
+ }
1958
+
1959
+ $rows = [];
1960
+ foreach ( $wpdb->last_result as $row ) {
1961
+ $rows[] = \get_object_vars( $row );
1962
+ }
1963
+
1964
+ return $rows;
1965
+ }
1966
+
1967
+ /**
1968
+ * Reset the Idiorm instance state
1969
+ */
1970
+ private function _reset_idiorm_state() {
1971
+ $this->_values = [];
1972
+ $this->_result_columns = [ '*' ];
1973
+ $this->_using_default_result_columns = true;
1974
+ }
1975
+
1976
+ /**
1977
+ * Return the raw data wrapped by this ORM
1978
+ * instance as an associative array. Column
1979
+ * names may optionally be supplied as arguments,
1980
+ * if so, only those keys will be returned.
1981
+ */
1982
+ public function as_array() {
1983
+ if ( \func_num_args() === 0 ) {
1984
+ return $this->_data;
1985
+ }
1986
+ $args = \func_get_args();
1987
+
1988
+ return \array_intersect_key( $this->_data, \array_flip( $args ) );
1989
+ }
1990
+
1991
+ /**
1992
+ * Return the value of a property of this object (database row)
1993
+ * or null if not present.
1994
+ *
1995
+ * If a column-names array is passed, it will return a associative array
1996
+ * with the value of each column or null if it is not present.
1997
+ *
1998
+ * @param $key
1999
+ *
2000
+ * @return array|mixed|null
2001
+ */
2002
+ public function get( $key ) {
2003
+ if ( \is_array( $key ) ) {
2004
+ $result = [];
2005
+ foreach ( $key as $column ) {
2006
+ $result[ $column ] = isset( $this->_data[ $column ] ) ? $this->_data[ $column ] : null;
2007
+ }
2008
+
2009
+ return $result;
2010
+ } else {
2011
+ return isset( $this->_data[ $key ] ) ? $this->_data[ $key ] : null;
2012
+ }
2013
+ }
2014
+
2015
+ /**
2016
+ * Return the name of the column in the database table which contains
2017
+ * the primary key ID of the row.
2018
+ */
2019
+ protected function _get_id_column_name() {
2020
+ if ( ! \is_null( $this->_instance_id_column ) ) {
2021
+ return $this->_instance_id_column;
2022
+ }
2023
+
2024
+ return 'id';
2025
+ }
2026
+
2027
+ /**
2028
+ * Get the primary key ID of this object.
2029
+ *
2030
+ * @param bool $disallow_null
2031
+ *
2032
+ * @return array|mixed|null
2033
+ * @throws \Exception
2034
+ */
2035
+ public function id( $disallow_null = false ) {
2036
+ $id = $this->get( $this->_get_id_column_name() );
2037
+ if ( $disallow_null ) {
2038
+ if ( \is_array( $id ) ) {
2039
+ foreach ( $id as $id_part ) {
2040
+ if ( $id_part === null ) {
2041
+ throw new \Exception( 'Primary key ID contains null value(s)' );
2042
+ }
2043
+ }
2044
+ } else {
2045
+ if ( $id === null ) {
2046
+ throw new \Exception( 'Primary key ID missing from row or is null' );
2047
+ }
2048
+ }
2049
+ }
2050
+
2051
+ return $id;
2052
+ }
2053
+
2054
+ /**
2055
+ * Set a property to a particular value on this object.
2056
+ * To set multiple properties at once, pass an associative array
2057
+ * as the first parameter and leave out the second parameter.
2058
+ * Flags the properties as 'dirty' so they will be saved to the
2059
+ * database when save() is called.
2060
+ *
2061
+ * @param $key
2062
+ * @param null $value
2063
+ *
2064
+ * @return ORM
2065
+ */
2066
+ public function set( $key, $value = null ) {
2067
+ return $this->_set_orm_property( $key, $value );
2068
+ }
2069
+
2070
+ /**
2071
+ * Set a property to a particular value on this object.
2072
+ * To set multiple properties at once, pass an associative array
2073
+ * as the first parameter and leave out the second parameter.
2074
+ * Flags the properties as 'dirty' so they will be saved to the
2075
+ * database when save() is called.
2076
+ *
2077
+ * @param string|array $key
2078
+ * @param string|null $value
2079
+ *
2080
+ * @return ORM
2081
+ */
2082
+ public function set_expr( $key, $value = null ) {
2083
+ return $this->_set_orm_property( $key, $value, true );
2084
+ }
2085
+
2086
+ /**
2087
+ * Set a property on the ORM object.
2088
+ *
2089
+ * @param string|array $key
2090
+ * @param string|null $value
2091
+ * @param bool $expr
2092
+ *
2093
+ * @return ORM
2094
+ */
2095
+ protected function _set_orm_property( $key, $value = null, $expr = false ) {
2096
+ if ( ! \is_array( $key ) ) {
2097
+ $key = [ $key => $value ];
2098
+ }
2099
+ foreach ( $key as $field => $value ) {
2100
+ $this->_data[ $field ] = $value;
2101
+ $this->_dirty_fields[ $field ] = $value;
2102
+ if ( false === $expr and isset( $this->_expr_fields[ $field ] ) ) {
2103
+ unset( $this->_expr_fields[ $field ] );
2104
+ } else {
2105
+ if ( true === $expr ) {
2106
+ $this->_expr_fields[ $field ] = true;
2107
+ }
2108
+ }
2109
+ }
2110
+
2111
+ return $this;
2112
+ }
2113
+
2114
+ /**
2115
+ * Check whether the given field has been changed since this
2116
+ * object was saved.
2117
+ *
2118
+ * @param $key
2119
+ *
2120
+ * @return bool
2121
+ */
2122
+ public function is_dirty( $key ) {
2123
+ return \array_key_exists( $key, $this->_dirty_fields );
2124
+ }
2125
+
2126
+ /**
2127
+ * Check whether the model was the result of a call to create() or not
2128
+ *
2129
+ * @return bool
2130
+ */
2131
+ public function is_new() {
2132
+ return $this->_is_new;
2133
+ }
2134
+
2135
+ /**
2136
+ * Save any fields which have been modified on this object
2137
+ * to the database.
2138
+ */
2139
+ public function save() {
2140
+ global $wpdb;
2141
+
2142
+ // remove any expression fields as they are already baked into the query
2143
+ $values = \array_values( \array_diff_key( $this->_dirty_fields, $this->_expr_fields ) );
2144
+ if ( ! $this->_is_new ) {
2145
+ // UPDATE
2146
+ // If there are no dirty values, do nothing
2147
+ if ( empty( $values ) && empty( $this->_expr_fields ) ) {
2148
+ return true;
2149
+ }
2150
+ $query = $this->_build_update();
2151
+ $id = $this->id( true );
2152
+ if ( \is_array( $id ) ) {
2153
+ $values = \array_merge( $values, \array_values( $id ) );
2154
+ } else {
2155
+ $values[] = $id;
2156
+ }
2157
+ } else {
2158
+ // INSERT
2159
+ $query = $this->_build_insert();
2160
+ }
2161
+ $success = self::_execute( $query, $values );
2162
+ // If we've just inserted a new record, set the ID of this object
2163
+ if ( $this->_is_new ) {
2164
+ $this->_is_new = false;
2165
+ if ( $this->count_null_id_columns() != 0 ) {
2166
+ $column = $this->_get_id_column_name();
2167
+ // if the primary key is compound, assign the last inserted id
2168
+ // to the first column
2169
+ if ( \is_array( $column ) ) {
2170
+ $column = \reset( $column );
2171
+ }
2172
+ $this->_data[ $column ] = $wpdb->insert_id;
2173
+ }
2174
+ }
2175
+ $this->_dirty_fields = $this->_expr_fields = [];
2176
+
2177
+ return $success;
2178
+ }
2179
+
2180
+ /**
2181
+ * Add a WHERE clause for every column that belongs to the primary key
2182
+ *
2183
+ * @param array $query The query.
2184
+ *
2185
+ * @return void
2186
+ */
2187
+ public function _add_id_column_conditions( &$query ) {
2188
+ $query[] = 'WHERE';
2189
+ $keys = \is_array( $this->_get_id_column_name() ) ? $this->_get_id_column_name() : [ $this->_get_id_column_name() ];
2190
+ $first = true;
2191
+ foreach ( $keys as $key ) {
2192
+ if ( $first ) {
2193
+ $first = false;
2194
+ } else {
2195
+ $query[] = 'AND';
2196
+ }
2197
+ $query[] = $this->_quote_identifier( $key );
2198
+ $query[] = '= %s';
2199
+ }
2200
+ }
2201
+
2202
+ /**
2203
+ * Build an UPDATE query
2204
+ *
2205
+ * @return string The update query.
2206
+ */
2207
+ protected function _build_update() {
2208
+ $query = [];
2209
+ $query[] = "UPDATE {$this->_quote_identifier($this->_table_name)} SET";
2210
+ $field_list = [];
2211
+ foreach ( $this->_dirty_fields as $key => $value ) {
2212
+ if ( ! \array_key_exists( $key, $this->_expr_fields ) ) {
2213
+ $value = '%s';
2214
+ }
2215
+ $field_list[] = "{$this->_quote_identifier($key)} = {$value}";
2216
+ }
2217
+ $query[] = \join( ', ', $field_list );
2218
+ $this->_add_id_column_conditions( $query );
2219
+
2220
+ return \join( ' ', $query );
2221
+ }
2222
+
2223
+ /**
2224
+ * Build an INSERT query
2225
+ *
2226
+ * @return string The insert query.
2227
+ */
2228
+ protected function _build_insert() {
2229
+ $query[] = 'INSERT INTO';
2230
+ $query[] = $this->_quote_identifier( $this->_table_name );
2231
+ $field_list = \array_map( [ $this, '_quote_identifier' ], \array_keys( $this->_dirty_fields ) );
2232
+ $query[] = '(' . \join( ', ', $field_list ) . ')';
2233
+ $query[] = 'VALUES';
2234
+ $placeholders = $this->_create_placeholders( $this->_dirty_fields );
2235
+ $query[] = "({$placeholders})";
2236
+
2237
+ return \join( ' ', $query );
2238
+ }
2239
+
2240
+ /**
2241
+ * Delete this record from the database
2242
+ *
2243
+ * @return string The delete query.
2244
+ *
2245
+ * @throws \Exception
2246
+ */
2247
+ public function delete() {
2248
+ $query = [ 'DELETE FROM', $this->_quote_identifier( $this->_table_name ) ];
2249
+ $this->_add_id_column_conditions( $query );
2250
+
2251
+ return self::_execute( \join( ' ', $query ), \is_array( $this->id( true ) ) ? \array_values( $this->id( true ) ) : [ $this->id( true ) ] );
2252
+ }
2253
+
2254
+ /**
2255
+ * Delete many records from the database
2256
+ */
2257
+ public function delete_many() {
2258
+ // Build and return the full DELETE statement by concatenating
2259
+ // the results of calling each separate builder method.
2260
+ $query = $this->_join_if_not_empty( ' ', [
2261
+ 'DELETE FROM',
2262
+ $this->_quote_identifier( $this->_table_name ),
2263
+ $this->_build_where(),
2264
+ ] );
2265
+
2266
+ return self::_execute( $query, $this->_values );
2267
+ }
2268
+ // --------------------- //
2269
+ // --- ArrayAccess --- //
2270
+ // --------------------- //
2271
+ /**
2272
+ * @param mixed $key
2273
+ *
2274
+ * @return bool
2275
+ */
2276
+ public function offsetExists( $key ) {
2277
+ return \array_key_exists( $key, $this->_data );
2278
+ }
2279
+
2280
+ /**
2281
+ * @param mixed $key
2282
+ *
2283
+ * @return array|mixed|null
2284
+ */
2285
+ public function offsetGet( $key ) {
2286
+ return $this->get( $key );
2287
+ }
2288
+
2289
+ /**
2290
+ * @param mixed $key
2291
+ * @param mixed $value
2292
+ */
2293
+ public function offsetSet( $key, $value ) {
2294
+ if ( \is_null( $key ) ) {
2295
+ return;
2296
+ }
2297
+ $this->set( $key, $value );
2298
+ }
2299
+
2300
+ /**
2301
+ * @param mixed $key
2302
+ */
2303
+ public function offsetUnset( $key ) {
2304
+ unset( $this->_data[ $key ] );
2305
+ unset( $this->_dirty_fields[ $key ] );
2306
+ }
2307
+ // --------------------- //
2308
+ // --- MAGIC METHODS --- //
2309
+ // --------------------- //
2310
+ /**
2311
+ * @param $key
2312
+ *
2313
+ * @return array|mixed|null
2314
+ */
2315
+ public function __get( $key ) {
2316
+ return $this->offsetGet( $key );
2317
+ }
2318
+
2319
+ /**
2320
+ * @param $key
2321
+ * @param $value
2322
+ */
2323
+ public function __set( $key, $value ) {
2324
+ $this->offsetSet( $key, $value );
2325
+ }
2326
+
2327
+ /**
2328
+ * @param $key
2329
+ */
2330
+ public function __unset( $key ) {
2331
+ $this->offsetUnset( $key );
2332
+ }
2333
+
2334
+ /**
2335
+ * @param $key
2336
+ *
2337
+ * @return bool
2338
+ */
2339
+ public function __isset( $key ) {
2340
+ return $this->offsetExists( $key );
2341
+ }
2342
+ }
lib/ruckusing-adapter.php ADDED
@@ -0,0 +1,1084 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Yoast model class.
4
+ *
5
+ * @package Yoast\WP\Lib
6
+ */
7
+
8
+ namespace Yoast\WP\Lib;
9
+
10
+ use YoastSEO_Vendor\Ruckusing_Adapter_ColumnDefinition;
11
+ use YoastSEO_Vendor\Ruckusing_Adapter_Interface;
12
+ use YoastSEO_Vendor\Ruckusing_Adapter_MySQL_Base;
13
+ use YoastSEO_Vendor\Ruckusing_Adapter_MySQL_TableDefinition;
14
+ use YoastSEO_Vendor\Ruckusing_Exception;
15
+ use YoastSEO_Vendor\Ruckusing_Util_Naming;
16
+
17
+ use const YoastSEO_Vendor\MYSQL_MAX_IDENTIFIER_LENGTH;
18
+ use const YoastSEO_Vendor\SQL_ALTER;
19
+ use const YoastSEO_Vendor\SQL_CREATE;
20
+ use const YoastSEO_Vendor\SQL_DELETE;
21
+ use const YoastSEO_Vendor\SQL_DROP;
22
+ use const YoastSEO_Vendor\SQL_INSERT;
23
+ use const YoastSEO_Vendor\SQL_RENAME;
24
+ use const YoastSEO_Vendor\SQL_SELECT;
25
+ use const YoastSEO_Vendor\SQL_SET;
26
+ use const YoastSEO_Vendor\SQL_SHOW;
27
+ use const YoastSEO_Vendor\SQL_UNKNOWN_QUERY_TYPE;
28
+ use const YoastSEO_Vendor\SQL_UPDATE;
29
+
30
+ /**
31
+ * Ruckusing_Adapter
32
+ */
33
+ class Ruckusing_Adapter extends Ruckusing_Adapter_MySQL_Base implements Ruckusing_Adapter_Interface {
34
+
35
+ /**
36
+ * Name of adapter
37
+ *
38
+ * @var string
39
+ */
40
+ private $_name = 'MySQL';
41
+ /**
42
+ * Tables
43
+ *
44
+ * @var array
45
+ */
46
+ private $_tables = [];
47
+ /**
48
+ * Tables_loaded
49
+ *
50
+ * @var boolean
51
+ */
52
+ private $_tables_loaded = false;
53
+ /**
54
+ * Version
55
+ *
56
+ * @var string
57
+ */
58
+ private $_version = '1.0';
59
+ /**
60
+ * Indicate if is in transaction
61
+ *
62
+ * @var boolean
63
+ */
64
+ private $_in_trx = false;
65
+ /**
66
+ * Creates an instance of Ruckusing_Adapter.
67
+ *
68
+ * @param array $config The configuration.
69
+ *
70
+ * @return Ruckusing_Adapter
71
+ */
72
+ public function __construct( $config ) {
73
+ $this->set_dsn( $config );
74
+ }
75
+ /**
76
+ * Get the current db name
77
+ *
78
+ * @return string
79
+ */
80
+ public function get_database_name() {
81
+ global $wpdb;
82
+ return $wpdb->dbname;
83
+ }
84
+ /**
85
+ * Check support for migrations
86
+ *
87
+ * @return boolean
88
+ */
89
+ public function supports_migrations() {
90
+ return true;
91
+ }
92
+ /**
93
+ * Get the column native types
94
+ *
95
+ * @return array
96
+ */
97
+ public function native_database_types() {
98
+ $types = [ 'primary_key' => [ 'name' => 'integer', 'limit' => 11, 'null' => false ], 'string' => [ 'name' => 'varchar', 'limit' => 255 ], 'text' => [ 'name' => 'text' ], 'tinytext' => [ 'name' => 'tinytext' ], 'mediumtext' => [ 'name' => 'mediumtext' ], 'integer' => [ 'name' => 'int', 'limit' => 11 ], 'tinyinteger' => [ 'name' => 'tinyint' ], 'smallinteger' => [ 'name' => 'smallint' ], 'mediuminteger' => [ 'name' => 'mediumint' ], 'biginteger' => [ 'name' => 'bigint' ], 'float' => [ 'name' => 'float' ], 'decimal' => [ 'name' => 'decimal', 'scale' => 0, 'precision' => 10 ], 'datetime' => [ 'name' => 'datetime' ], 'timestamp' => [ 'name' => 'timestamp' ], 'time' => [ 'name' => 'time' ], 'date' => [ 'name' => 'date' ], 'binary' => [ 'name' => 'blob' ], 'tinybinary' => [ 'name' => 'tinyblob' ], 'mediumbinary' => [ 'name' => 'mediumblob' ], 'longbinary' => [ 'name' => 'longblob' ], 'boolean' => [ 'name' => 'tinyint', 'limit' => 1 ], 'enum' => [ 'name' => 'enum', 'values' => [] ], 'uuid' => [ 'name' => 'char', 'limit' => 36 ], 'char' => [ 'name' => 'char' ] ];
99
+ return $types;
100
+ }
101
+ /**
102
+ * Create the schema table, if necessary
103
+ */
104
+ public function create_schema_version_table() {
105
+ if ( ! $this->has_table( $this->get_schema_version_table_name() ) ) {
106
+ $t = $this->create_table( $this->get_schema_version_table_name(), [ 'id' => false ] );
107
+ $t->column( 'version', 'string' );
108
+ $t->finish();
109
+ $this->add_index( $this->get_schema_version_table_name(), 'version', [ 'unique' => true ] );
110
+ }
111
+ }
112
+ /**
113
+ * Start Transaction
114
+ */
115
+ public function start_transaction() {
116
+ if ( $this->inTransaction() === false ) {
117
+ $this->beginTransaction();
118
+ }
119
+ }
120
+ /**
121
+ * Commit Transaction
122
+ */
123
+ public function commit_transaction() {
124
+ if ( $this->inTransaction() ) {
125
+ $this->commit();
126
+ }
127
+ }
128
+ /**
129
+ * Rollback Transaction
130
+ */
131
+ public function rollback_transaction() {
132
+ if ( $this->inTransaction() ) {
133
+ $this->rollback();
134
+ }
135
+ }
136
+ /**
137
+ * Quote a table name string
138
+ *
139
+ * @param string $str table name.
140
+ * @return string
141
+ */
142
+ public function quote_table( $str ) {
143
+ return '`' . $str . '`';
144
+ }
145
+ /**
146
+ * Column definition
147
+ *
148
+ * @param string $column_name The column name.
149
+ * @param string $type The type of the column.
150
+ * @param array $options Column options.
151
+ *
152
+ * @return string
153
+ */
154
+ public function column_definition( $column_name, $type, $options = null ) {
155
+ $col = new Ruckusing_Adapter_ColumnDefinition( $this, $column_name, $type, $options );
156
+ return $col->__toString();
157
+ }
158
+ // -------- DATABASE LEVEL OPERATIONS
159
+ /**
160
+ * Check if a db exists
161
+ *
162
+ * @param string $db The db name.
163
+ *
164
+ * @return boolean
165
+ */
166
+ public function database_exists( $db ) {
167
+ $ddl = 'SHOW DATABASES';
168
+ $result = $this->select_all( $ddl );
169
+ if ( \count( $result ) == 0 ) {
170
+ return false;
171
+ }
172
+ foreach ( $result as $dbrow ) {
173
+ if ( $dbrow['Database'] == $db ) {
174
+ return true;
175
+ }
176
+ }
177
+ return false;
178
+ }
179
+ /**
180
+ * Create a database
181
+ *
182
+ * @param string $db the db name
183
+ *
184
+ * @return boolean
185
+ */
186
+ public function create_database( $db ) {
187
+ if ( $this->database_exists( $db ) ) {
188
+ return false;
189
+ }
190
+ $ddl = \sprintf( 'CREATE DATABASE %s', $this->identifier( $db ) );
191
+ $result = $this->query( $ddl );
192
+ return $result === true;
193
+ }
194
+ /**
195
+ * Drop a database
196
+ *
197
+ * @param string $db the db name
198
+ *
199
+ * @return boolean
200
+ */
201
+ public function drop_database( $db ) {
202
+ if ( ! $this->database_exists( $db ) ) {
203
+ return false;
204
+ }
205
+ $ddl = \sprintf( 'DROP DATABASE IF EXISTS %s', $this->identifier( $db ) );
206
+ $result = $this->query( $ddl );
207
+ return $result === true;
208
+ }
209
+ /**
210
+ * Dump the complete schema of the DB. This is really just all of the
211
+ * CREATE TABLE statements for all of the tables in the DB.
212
+ * NOTE: this does NOT include any INSERT statements or the actual data
213
+ * (that is, this method is NOT a replacement for mysqldump)
214
+ *
215
+ * @param string $output_file the filepath to output to
216
+ *
217
+ * @return int|FALSE
218
+ */
219
+ public function schema( $output_file ) {
220
+ $final = '';
221
+ $views = '';
222
+ $this->load_tables( true );
223
+ foreach ( $this->_tables as $tbl => $idx ) {
224
+ if ( $tbl == 'schema_info' ) {
225
+ continue;
226
+ }
227
+ $stmt = \sprintf( 'SHOW CREATE TABLE %s', $this->identifier( $tbl ) );
228
+ $result = $this->query( $stmt );
229
+ if ( \is_array( $result ) && \count( $result ) == 1 ) {
230
+ $row = $result[0];
231
+ if ( \count( $row ) == 2 ) {
232
+ if ( isset( $row['Create Table'] ) ) {
233
+ $final .= $row['Create Table'] . ";\n\n";
234
+ } elseif ( isset( $row['Create View'] ) ) {
235
+ $views .= $row['Create View'] . ";\n\n";
236
+ }
237
+ }
238
+ }
239
+ }
240
+ $data = $final . $views;
241
+ return \file_put_contents( $output_file, $data, \LOCK_EX );
242
+ }
243
+ /**
244
+ * Check if a table exists
245
+ *
246
+ * @param string $tbl the table name
247
+ * @param boolean $reload_tables reload table or not
248
+ *
249
+ * @return boolean
250
+ */
251
+ public function table_exists( $tbl, $reload_tables = false ) {
252
+ $this->load_tables( $reload_tables );
253
+ return \array_key_exists( $tbl, $this->_tables );
254
+ }
255
+ /**
256
+ * Wrapper to execute a query
257
+ *
258
+ * @param string $query query to run
259
+ *
260
+ * @return boolean
261
+ */
262
+ public function execute( $query ) {
263
+ return $this->query( $query );
264
+ }
265
+ /**
266
+ * Execute a query
267
+ *
268
+ * @param string $query query to run
269
+ *
270
+ * @throws Ruckusing_Exception
271
+ * @return boolean
272
+ */
273
+ public function query( $query ) {
274
+ global $wpdb;
275
+
276
+ $query_type = $this->determine_query_type( $query );
277
+ $data = [];
278
+ if ( $query_type == SQL_SELECT || $query_type == SQL_SHOW ) {
279
+ $data = $wpdb->get_results( $query, ARRAY_A );
280
+ if ( $this->isError( $data ) ) {
281
+ throw new Ruckusing_Exception( \sprintf( "Error executing 'query' with:\n%s\n\nReason: %s\n\n", $query, $wpdb->last_error ), Ruckusing_Exception::QUERY_ERROR );
282
+ }
283
+ return $data;
284
+ } else {
285
+ // INSERT, DELETE, etc...
286
+ $res = $wpdb->query( $query );
287
+ if ( $this->isError( $res ) ) {
288
+ throw new Ruckusing_Exception( \sprintf( "Error executing 'query' with:\n%s\n\nReason: %s\n\n", $query, $wpdb->last_error ), Ruckusing_Exception::QUERY_ERROR );
289
+ }
290
+ if ( $query_type == SQL_INSERT ) {
291
+ return $wpdb->insert_id;
292
+ }
293
+ return true;
294
+ }
295
+ }
296
+ /**
297
+ * Execute several queries
298
+ *
299
+ * @param string $queries queries to run
300
+ *
301
+ * @throws Ruckusing_Exception
302
+ * @return boolean
303
+ */
304
+ public function multi_query( $queries ) {
305
+ if ( \defined( 'YOAST_ENVIRONMENT' ) && YOAST_ENVIRONMENT !== 'production' ) {
306
+ throw new Ruckusing_Exception( 'WPDB does not support multi_query.', Ruckusing_Exception::QUERY_ERROR );
307
+ }
308
+ return false;
309
+ }
310
+ /**
311
+ * Select one
312
+ *
313
+ * @param string $query query to run
314
+ *
315
+ * @throws Ruckusing_Exception
316
+ * @return array
317
+ */
318
+ public function select_one( $query ) {
319
+ global $wpdb;
320
+
321
+ $query_type = $this->determine_query_type( $query );
322
+ if ( $query_type == SQL_SELECT || $query_type == SQL_SHOW ) {
323
+ $res = $wpdb->query( $query );
324
+ if ( $this->isError( $res ) ) {
325
+ throw new Ruckusing_Exception( \sprintf( "Error executing 'query' with:\n%s\n\nReason: %s\n\n", $query, $wpdb->last_error ), Ruckusing_Exception::QUERY_ERROR );
326
+ }
327
+ return $wpdb->last_result;
328
+ } else {
329
+ throw new Ruckusing_Exception( "Query for select_one() is not one of SELECT or SHOW: {$query}", Ruckusing_Exception::QUERY_ERROR );
330
+ }
331
+ }
332
+ /**
333
+ * Select all
334
+ *
335
+ * @param string $query query to run
336
+ *
337
+ * @return array
338
+ */
339
+ public function select_all( $query ) {
340
+ return $this->query( $query );
341
+ }
342
+ /**
343
+ * Use this method for non-SELECT queries
344
+ * Or anything where you dont necessarily expect a result string, e.g. DROPs, CREATEs, etc.
345
+ *
346
+ * @param string $ddl query to run
347
+ *
348
+ * @return boolean
349
+ */
350
+ public function execute_ddl( $ddl ) {
351
+ $result = $this->query( $ddl );
352
+ return true;
353
+ }
354
+ /**
355
+ * Drop table
356
+ *
357
+ * @param string $tbl the table name
358
+ *
359
+ * @return boolean
360
+ */
361
+ public function drop_table( $tbl ) {
362
+ $ddl = \sprintf( 'DROP TABLE IF EXISTS %s', $this->identifier( $tbl ) );
363
+ $result = $this->query( $ddl );
364
+ return true;
365
+ }
366
+ /**
367
+ * Create table
368
+ *
369
+ * @param string $table_name the table name
370
+ * @param array $options the options
371
+ * @return bool|Ruckusing_Adapter_MySQL_TableDefinition
372
+ */
373
+ public function create_table( $table_name, $options = [] ) {
374
+ return new Ruckusing_Adapter_MySQL_TableDefinition( $this, $table_name, $options );
375
+ }
376
+ /**
377
+ * Escape a string for mysql
378
+ *
379
+ * @param string $str the string
380
+ *
381
+ * @return string
382
+ */
383
+ public function quote_string( $str ) {
384
+ global $wpdb;
385
+ return $wpdb->_escape( $str );
386
+ }
387
+ /**
388
+ * Quote a string
389
+ *
390
+ * @param string $str the string
391
+ *
392
+ * @return string
393
+ */
394
+ public function identifier( $str ) {
395
+ return '`' . $str . '`';
396
+ }
397
+ /**
398
+ * Quote a string
399
+ *
400
+ * @param string $value the string
401
+ * @param string $column the column
402
+ *
403
+ * @return string
404
+ */
405
+ public function quote( $value, $column = null ) {
406
+ return $this->quote_string( $value );
407
+ }
408
+ /**
409
+ * Rename a table
410
+ *
411
+ * @param string $name the current table name
412
+ * @param string $new_name the new table name
413
+ *
414
+ * @throws Ruckusing_Exception
415
+ * @return boolean
416
+ */
417
+ public function rename_table( $name, $new_name ) {
418
+ if ( empty( $name ) ) {
419
+ throw new Ruckusing_Exception( 'Missing original column name parameter', Ruckusing_Exception::INVALID_ARGUMENT );
420
+ }
421
+ if ( empty( $new_name ) ) {
422
+ throw new Ruckusing_Exception( 'Missing new column name parameter', Ruckusing_Exception::INVALID_ARGUMENT );
423
+ }
424
+ $sql = \sprintf( 'RENAME TABLE %s TO %s', $this->identifier( $name ), $this->identifier( $new_name ) );
425
+ return $this->execute_ddl( $sql );
426
+ }
427
+ // create_table
428
+ /**
429
+ * Add a column
430
+ *
431
+ * @param string $table_name the table name
432
+ * @param string $column_name the column name
433
+ * @param string $type the column type
434
+ * @param array $options column options
435
+ *
436
+ * @throws Ruckusing_Exception
437
+ * @return boolean
438
+ */
439
+ public function add_column( $table_name, $column_name, $type, $options = [] ) {
440
+ if ( empty( $table_name ) ) {
441
+ throw new Ruckusing_Exception( 'Missing table name parameter', Ruckusing_Exception::INVALID_ARGUMENT );
442
+ }
443
+ if ( empty( $column_name ) ) {
444
+ throw new Ruckusing_Exception( 'Missing column name parameter', Ruckusing_Exception::INVALID_ARGUMENT );
445
+ }
446
+ if ( empty( $type ) ) {
447
+ throw new Ruckusing_Exception( 'Missing type parameter', Ruckusing_Exception::INVALID_ARGUMENT );
448
+ }
449
+ // default types
450
+ if ( ! \array_key_exists( 'limit', $options ) ) {
451
+ $options['limit'] = null;
452
+ }
453
+ if ( ! \array_key_exists( 'precision', $options ) ) {
454
+ $options['precision'] = null;
455
+ }
456
+ if ( ! \array_key_exists( 'scale', $options ) ) {
457
+ $options['scale'] = null;
458
+ }
459
+ $sql = \sprintf( 'ALTER TABLE %s ADD `%s` %s', $this->identifier( $table_name ), $column_name, $this->type_to_sql( $type, $options ) );
460
+ $sql .= $this->add_column_options( $type, $options );
461
+ return $this->execute_ddl( $sql );
462
+ }
463
+ // add_column
464
+ /**
465
+ * Drop a column
466
+ *
467
+ * @param string $table_name the table name
468
+ * @param string $column_name the column name
469
+ *
470
+ * @return boolean
471
+ */
472
+ public function remove_column( $table_name, $column_name ) {
473
+ $sql = \sprintf( 'ALTER TABLE %s DROP COLUMN %s', $this->identifier( $table_name ), $this->identifier( $column_name ) );
474
+ return $this->execute_ddl( $sql );
475
+ }
476
+ // remove_column
477
+ /**
478
+ * Rename a column
479
+ *
480
+ * @param string $table_name the table name
481
+ * @param string $column_name the column name
482
+ * @param string $new_column_name the new column name
483
+ *
484
+ * @throws Ruckusing_Exception
485
+ * @return boolean
486
+ */
487
+ public function rename_column( $table_name, $column_name, $new_column_name ) {
488
+ if ( empty( $table_name ) ) {
489
+ throw new Ruckusing_Exception( 'Missing table name parameter', Ruckusing_Exception::INVALID_ARGUMENT );
490
+ }
491
+ if ( empty( $column_name ) ) {
492
+ throw new Ruckusing_Exception( 'Missing original column name parameter', Ruckusing_Exception::INVALID_ARGUMENT );
493
+ }
494
+ if ( empty( $new_column_name ) ) {
495
+ throw new Ruckusing_Exception( 'Missing new column name parameter', Ruckusing_Exception::INVALID_ARGUMENT );
496
+ }
497
+ $column_info = $this->column_info( $table_name, $column_name );
498
+ $current_type = $column_info['type'];
499
+ $sql = \sprintf( 'ALTER TABLE %s CHANGE %s %s %s', $this->identifier( $table_name ), $this->identifier( $column_name ), $this->identifier( $new_column_name ), $current_type );
500
+ $sql .= $this->add_column_options( $current_type, $column_info );
501
+ return $this->execute_ddl( $sql );
502
+ }
503
+ // rename_column
504
+ /**
505
+ * Change a column
506
+ *
507
+ * @param string $table_name the table name
508
+ * @param string $column_name the column name
509
+ * @param string $type the column type
510
+ * @param array $options column options
511
+ *
512
+ * @throws Ruckusing_Exception
513
+ * @return boolean
514
+ */
515
+ public function change_column( $table_name, $column_name, $type, $options = [] ) {
516
+ if ( empty( $table_name ) ) {
517
+ throw new Ruckusing_Exception( 'Missing table name parameter', Ruckusing_Exception::INVALID_ARGUMENT );
518
+ }
519
+ if ( empty( $column_name ) ) {
520
+ throw new Ruckusing_Exception( 'Missing original column name parameter', Ruckusing_Exception::INVALID_ARGUMENT );
521
+ }
522
+ if ( empty( $type ) ) {
523
+ throw new Ruckusing_Exception( 'Missing type parameter', Ruckusing_Exception::INVALID_ARGUMENT );
524
+ }
525
+ $column_info = $this->column_info( $table_name, $column_name );
526
+ // default types
527
+ if ( ! \array_key_exists( 'limit', $options ) ) {
528
+ $options['limit'] = null;
529
+ }
530
+ if ( ! \array_key_exists( 'precision', $options ) ) {
531
+ $options['precision'] = null;
532
+ }
533
+ if ( ! \array_key_exists( 'scale', $options ) ) {
534
+ $options['scale'] = null;
535
+ }
536
+ $sql = \sprintf( 'ALTER TABLE `%s` CHANGE `%s` `%s` %s', $table_name, $column_name, $column_name, $this->type_to_sql( $type, $options ) );
537
+ $sql .= $this->add_column_options( $type, $options );
538
+ return $this->execute_ddl( $sql );
539
+ }
540
+ // change_column
541
+ /**
542
+ * Get a column info
543
+ *
544
+ * @param string $table the table name
545
+ * @param string $column the column name
546
+ *
547
+ * @throws Ruckusing_Exception
548
+ * @return array
549
+ */
550
+ public function column_info( $table, $column ) {
551
+ if ( empty( $table ) ) {
552
+ throw new Ruckusing_Exception( 'Missing table name parameter', Ruckusing_Exception::INVALID_ARGUMENT );
553
+ }
554
+ if ( empty( $column ) ) {
555
+ throw new Ruckusing_Exception( 'Missing original column name parameter', Ruckusing_Exception::INVALID_ARGUMENT );
556
+ }
557
+ try {
558
+ $sql = \sprintf( "SHOW FULL COLUMNS FROM %s LIKE '%s'", $this->identifier( $table ), $column );
559
+ $result = $this->select_one( $sql );
560
+ if ( \is_array( $result ) ) {
561
+ // lowercase key names
562
+ $result = \array_change_key_case( $result, \CASE_LOWER );
563
+ }
564
+ return $result;
565
+ } catch (\Exception $e) {
566
+ return null;
567
+ }
568
+ }
569
+ /**
570
+ * Add an index
571
+ *
572
+ * @param string $table_name the table name
573
+ * @param string $column_name the column name
574
+ * @param array $options index options
575
+ *
576
+ * @throws Ruckusing_Exception
577
+ * @return boolean
578
+ */
579
+ public function add_index( $table_name, $column_name, $options = [] ) {
580
+ if ( empty( $table_name ) ) {
581
+ throw new Ruckusing_Exception( 'Missing table name parameter', Ruckusing_Exception::INVALID_ARGUMENT );
582
+ }
583
+ if ( empty( $column_name ) ) {
584
+ throw new Ruckusing_Exception( 'Missing column name parameter', Ruckusing_Exception::INVALID_ARGUMENT );
585
+ }
586
+ // unique index?
587
+ if ( \is_array( $options ) && \array_key_exists( 'unique', $options ) && $options['unique'] === true ) {
588
+ $unique = true;
589
+ } else {
590
+ $unique = false;
591
+ }
592
+ // did the user specify an index name?
593
+ if ( \is_array( $options ) && \array_key_exists( 'name', $options ) ) {
594
+ $index_name = $options['name'];
595
+ } else {
596
+ $index_name = Ruckusing_Util_Naming::index_name( $table_name, $column_name );
597
+ }
598
+ if ( \strlen( $index_name ) > MYSQL_MAX_IDENTIFIER_LENGTH ) {
599
+ $msg = 'The auto-generated index name is too long for MySQL (max is 64 chars). ';
600
+ $msg .= "Considering using 'name' option parameter to specify a custom name for this index.";
601
+ $msg .= ' Note: you will also need to specify';
602
+ $msg .= ' this custom name in a drop_index() - if you have one.';
603
+ throw new Ruckusing_Exception( $msg, Ruckusing_Exception::INVALID_INDEX_NAME );
604
+ }
605
+ if ( ! \is_array( $column_name ) ) {
606
+ $column_names = [ $column_name ];
607
+ } else {
608
+ $column_names = $column_name;
609
+ }
610
+ $cols = [];
611
+ foreach ( $column_names as $name ) {
612
+ $cols[] = $this->identifier( $name );
613
+ }
614
+ $sql = \sprintf( 'CREATE %sINDEX %s ON %s(%s)', $unique ? 'UNIQUE ' : '', $this->identifier( $index_name ), $this->identifier( $table_name ), \join( ', ', $cols ) );
615
+ return $this->execute_ddl( $sql );
616
+ }
617
+ /**
618
+ * Drop an index
619
+ *
620
+ * @param string $table_name the table name
621
+ * @param string $column_name the column name
622
+ * @param array $options index options
623
+ *
624
+ * @throws Ruckusing_Exception
625
+ * @return boolean
626
+ */
627
+ public function remove_index( $table_name, $column_name, $options = [] ) {
628
+ if ( empty( $table_name ) ) {
629
+ throw new Ruckusing_Exception( 'Missing table name parameter', Ruckusing_Exception::INVALID_ARGUMENT );
630
+ }
631
+ if ( empty( $column_name ) ) {
632
+ throw new Ruckusing_Exception( 'Missing column name parameter', Ruckusing_Exception::INVALID_ARGUMENT );
633
+ }
634
+ // did the user specify an index name?
635
+ if ( \is_array( $options ) && \array_key_exists( 'name', $options ) ) {
636
+ $index_name = $options['name'];
637
+ } else {
638
+ $index_name = Ruckusing_Util_Naming::index_name( $table_name, $column_name );
639
+ }
640
+ $sql = \sprintf( 'DROP INDEX %s ON %s', $this->identifier( $index_name ), $this->identifier( $table_name ) );
641
+ return $this->execute_ddl( $sql );
642
+ }
643
+ /**
644
+ * Add timestamps
645
+ *
646
+ * @param string $table_name The table name
647
+ * @param string $created_column_name Created at column name
648
+ * @param string $updated_column_name Updated at column name
649
+ *
650
+ * @return boolean
651
+ */
652
+ public function add_timestamps( $table_name, $created_column_name, $updated_column_name ) {
653
+ if ( empty( $table_name ) ) {
654
+ throw new Ruckusing_Exception( 'Missing table name parameter', Ruckusing_Exception::INVALID_ARGUMENT );
655
+ }
656
+ if ( empty( $created_column_name ) ) {
657
+ throw new Ruckusing_Exception( 'Missing created at column name parameter', Ruckusing_Exception::INVALID_ARGUMENT );
658
+ }
659
+ if ( empty( $updated_column_name ) ) {
660
+ throw new Ruckusing_Exception( 'Missing updated at column name parameter', Ruckusing_Exception::INVALID_ARGUMENT );
661
+ }
662
+ $created_at = $this->add_column( $table_name, $created_column_name, 'datetime' );
663
+ $updated_at = $this->add_column( $table_name, $updated_column_name, 'timestamp', [ 'null' => false, 'default' => 'CURRENT_TIMESTAMP', 'extra' => 'ON UPDATE CURRENT_TIMESTAMP' ] );
664
+ return $created_at && $updated_at;
665
+ }
666
+ /**
667
+ * Remove timestamps
668
+ *
669
+ * @param string $table_name The table name
670
+ * @param string $created_column_name Created at column name
671
+ * @param string $updated_column_name Updated at column name
672
+ *
673
+ * @return boolean
674
+ */
675
+ public function remove_timestamps( $table_name, $created_column_name, $updated_column_name ) {
676
+ if ( empty( $table_name ) ) {
677
+ throw new Ruckusing_Exception( 'Missing table name parameter', Ruckusing_Exception::INVALID_ARGUMENT );
678
+ }
679
+ if ( empty( $created_column_name ) ) {
680
+ throw new Ruckusing_Exception( 'Missing created at column name parameter', Ruckusing_Exception::INVALID_ARGUMENT );
681
+ }
682
+ if ( empty( $updated_column_name ) ) {
683
+ throw new Ruckusing_Exception( 'Missing updated at column name parameter', Ruckusing_Exception::INVALID_ARGUMENT );
684
+ }
685
+ $updated_at = $this->remove_column( $table_name, $created_column_name );
686
+ $created_at = $this->remove_column( $table_name, $updated_column_name );
687
+ return $created_at && $updated_at;
688
+ }
689
+ /**
690
+ * Check an index
691
+ *
692
+ * @param string $table_name the table name
693
+ * @param string $column_name the column name
694
+ * @param array $options index options
695
+ *
696
+ * @throws Ruckusing_Exception
697
+ * @return boolean
698
+ */
699
+ public function has_index( $table_name, $column_name, $options = [] ) {
700
+ if ( empty( $table_name ) ) {
701
+ throw new Ruckusing_Exception( 'Missing table name parameter', Ruckusing_Exception::INVALID_ARGUMENT );
702
+ }
703
+ if ( empty( $column_name ) ) {
704
+ throw new Ruckusing_Exception( 'Missing column name parameter', Ruckusing_Exception::INVALID_ARGUMENT );
705
+ }
706
+ // did the user specify an index name?
707
+ if ( \is_array( $options ) && \array_key_exists( 'name', $options ) ) {
708
+ $index_name = $options['name'];
709
+ } else {
710
+ $index_name = Ruckusing_Util_Naming::index_name( $table_name, $column_name );
711
+ }
712
+ $indexes = $this->indexes( $table_name );
713
+ foreach ( $indexes as $idx ) {
714
+ if ( $idx['name'] == $index_name ) {
715
+ return true;
716
+ }
717
+ }
718
+ return false;
719
+ }
720
+ /**
721
+ * Return all indexes of a table
722
+ *
723
+ * @param string $table_name the table name
724
+ *
725
+ * @return array
726
+ */
727
+ public function indexes( $table_name ) {
728
+ $sql = \sprintf( 'SHOW KEYS FROM %s', $this->identifier( $table_name ) );
729
+ $result = $this->select_all( $sql );
730
+ $indexes = [];
731
+ $cur_idx = null;
732
+ foreach ( $result as $row ) {
733
+ // skip primary
734
+ if ( $row['Key_name'] == 'PRIMARY' ) {
735
+ continue;
736
+ }
737
+ $cur_idx = $row['Key_name'];
738
+ $indexes[] = [ 'name' => $row['Key_name'], 'unique' => (int) $row['Non_unique'] == 0 ? true : false ];
739
+ }
740
+ return $indexes;
741
+ }
742
+ /**
743
+ * Convert type to sql
744
+ * $limit = null, $precision = null, $scale = null
745
+ *
746
+ * @param string $type the native type
747
+ * @param array $options
748
+ *
749
+ * @throws Ruckusing_Exception
750
+ * @return string
751
+ */
752
+ public function type_to_sql( $type, $options = [] ) {
753
+ $natives = $this->native_database_types();
754
+ if ( ! \array_key_exists( $type, $natives ) ) {
755
+ $error = \sprintf( "Error:I dont know what column type of '%s' maps to for MySQL.", $type );
756
+ $error .= "\nYou provided: {$type}\n";
757
+ $error .= "Valid types are: \n";
758
+ $types = \array_keys( $natives );
759
+ foreach ( $types as $t ) {
760
+ if ( $t == 'primary_key' ) {
761
+ continue;
762
+ }
763
+ $error .= "\t{$t}\n";
764
+ }
765
+ throw new Ruckusing_Exception( $error, Ruckusing_Exception::INVALID_ARGUMENT );
766
+ }
767
+ $scale = null;
768
+ $precision = null;
769
+ $limit = null;
770
+ if ( isset( $options['precision'] ) ) {
771
+ $precision = $options['precision'];
772
+ }
773
+ if ( isset( $options['scale'] ) ) {
774
+ $scale = $options['scale'];
775
+ }
776
+ if ( isset( $options['limit'] ) ) {
777
+ $limit = $options['limit'];
778
+ }
779
+ if ( isset( $options['values'] ) ) {
780
+ $values = $options['values'];
781
+ }
782
+ $native_type = $natives[ $type ];
783
+ if ( \is_array( $native_type ) && \array_key_exists( 'name', $native_type ) ) {
784
+ $column_type_sql = $native_type['name'];
785
+ } else {
786
+ return $native_type;
787
+ }
788
+ if ( $type == 'decimal' ) {
789
+ // ignore limit, use precison and scale
790
+ if ( $precision == null && \array_key_exists( 'precision', $native_type ) ) {
791
+ $precision = $native_type['precision'];
792
+ }
793
+ if ( $scale == null && \array_key_exists( 'scale', $native_type ) ) {
794
+ $scale = $native_type['scale'];
795
+ }
796
+ if ( $precision != null ) {
797
+ if ( \is_int( $scale ) ) {
798
+ $column_type_sql .= \sprintf( '(%d, %d)', $precision, $scale );
799
+ } else {
800
+ $column_type_sql .= \sprintf( '(%d)', $precision );
801
+ }
802
+ // scale
803
+ } else {
804
+ if ( $scale ) {
805
+ throw new Ruckusing_Exception( 'Error adding decimal column: precision cannot be empty if scale is specified', Ruckusing_Exception::INVALID_ARGUMENT );
806
+ }
807
+ }
808
+ // precision
809
+ } elseif ( $type == 'float' ) {
810
+ // ignore limit, use precison and scale
811
+ if ( $precision == null && \array_key_exists( 'precision', $native_type ) ) {
812
+ $precision = $native_type['precision'];
813
+ }
814
+ if ( $scale == null && \array_key_exists( 'scale', $native_type ) ) {
815
+ $scale = $native_type['scale'];
816
+ }
817
+ if ( $precision != null ) {
818
+ if ( \is_int( $scale ) ) {
819
+ $column_type_sql .= \sprintf( '(%d, %d)', $precision, $scale );
820
+ } else {
821
+ $column_type_sql .= \sprintf( '(%d)', $precision );
822
+ }
823
+ // scale
824
+ } else {
825
+ if ( $scale ) {
826
+ throw new Ruckusing_Exception( 'Error adding float column: precision cannot be empty if scale is specified', Ruckusing_Exception::INVALID_ARGUMENT );
827
+ }
828
+ }
829
+ // precision
830
+ } elseif ( $type == 'enum' ) {
831
+ if ( empty( $values ) ) {
832
+ throw new Ruckusing_Exception( 'Error adding enum column: there must be at least one value defined', Ruckusing_Exception::INVALID_ARGUMENT );
833
+ } else {
834
+ $column_type_sql .= \sprintf( "('%s')", \implode( "','", \array_map( [ $this, 'quote_string' ], $values ) ) );
835
+ }
836
+ }
837
+ // not a decimal column
838
+ if ( $limit == null && \array_key_exists( 'limit', $native_type ) ) {
839
+ $limit = $native_type['limit'];
840
+ }
841
+ if ( $limit ) {
842
+ $column_type_sql .= \sprintf( '(%d)', $limit );
843
+ }
844
+ return $column_type_sql;
845
+ }
846
+ /**
847
+ * Add column options
848
+ *
849
+ * @param string $type the native type
850
+ * @param array $options
851
+ *
852
+ * @throws Ruckusing_Exception
853
+ * @return string
854
+ */
855
+ public function add_column_options( $type, $options ) {
856
+ $sql = '';
857
+ if ( ! \is_array( $options ) ) {
858
+ return $sql;
859
+ }
860
+ if ( \array_key_exists( 'unsigned', $options ) && $options['unsigned'] === true ) {
861
+ $sql .= ' UNSIGNED';
862
+ }
863
+ if ( \array_key_exists( 'character', $options ) ) {
864
+ $sql .= \sprintf( ' CHARACTER SET %s', $this->identifier( $options['character'] ) );
865
+ }
866
+ if ( \array_key_exists( 'collate', $options ) ) {
867
+ $sql .= \sprintf( ' COLLATE %s', $this->identifier( $options['collate'] ) );
868
+ }
869
+ if ( \array_key_exists( 'auto_increment', $options ) && $options['auto_increment'] === true ) {
870
+ $sql .= ' auto_increment';
871
+ }
872
+ if ( \array_key_exists( 'default', $options ) && $options['default'] !== null ) {
873
+ if ( $this->is_sql_method_call( $options['default'] ) ) {
874
+ // $default_value = $options['default'];
875
+ throw new Ruckusing_Exception( 'MySQL does not support function calls as default values, constants only.', Ruckusing_Exception::INVALID_ARGUMENT );
876
+ }
877
+ if ( \is_int( $options['default'] ) ) {
878
+ $default_format = '%d';
879
+ } elseif ( \is_bool( $options['default'] ) ) {
880
+ $default_format = "'%d'";
881
+ } elseif ( $options['default'] == 'CURRENT_TIMESTAMP' ) {
882
+ $default_format = '%s';
883
+ } else {
884
+ $default_format = "'%s'";
885
+ }
886
+ $default_value = \sprintf( $default_format, $options['default'] );
887
+ $sql .= \sprintf( ' DEFAULT %s', $default_value );
888
+ }
889
+ if ( \array_key_exists( 'null', $options ) ) {
890
+ if ( $options['null'] === false || $options['null'] === 'NO' ) {
891
+ $sql .= ' NOT NULL';
892
+ } elseif ( 'timestamp' === $type ) {
893
+ $sql .= ' NULL';
894
+ }
895
+ }
896
+ if ( \array_key_exists( 'comment', $options ) ) {
897
+ $sql .= \sprintf( " COMMENT '%s'", $this->quote_string( $options['comment'] ) );
898
+ }
899
+ if ( \array_key_exists( 'extra', $options ) ) {
900
+ $sql .= \sprintf( ' %s', $this->quote_string( $options['extra'] ) );
901
+ }
902
+ if ( \array_key_exists( 'after', $options ) ) {
903
+ $sql .= \sprintf( ' AFTER %s', $this->identifier( $options['after'] ) );
904
+ }
905
+ return $sql;
906
+ }
907
+ /**
908
+ * Set current version
909
+ *
910
+ * @param string $version the version
911
+ *
912
+ * @return boolean
913
+ */
914
+ public function set_current_version( $version ) {
915
+ $sql = \sprintf( "INSERT INTO %s (version) VALUES ('%s')", $this->get_schema_version_table_name(), $version );
916
+ return $this->execute_ddl( $sql );
917
+ }
918
+ /**
919
+ * remove a version
920
+ *
921
+ * @param string $version the version
922
+ *
923
+ * @return boolean
924
+ */
925
+ public function remove_version( $version ) {
926
+ $sql = \sprintf( "DELETE FROM %s WHERE version = '%s'", $this->get_schema_version_table_name(), $version );
927
+ return $this->execute_ddl( $sql );
928
+ }
929
+ /**
930
+ * Return a message displaying the current version
931
+ *
932
+ * @return string
933
+ */
934
+ public function __toString() {
935
+ return 'Ruckusing_Adapter, version ' . $this->_version;
936
+ }
937
+ // -----------------------------------
938
+ // PRIVATE METHODS
939
+ // -----------------------------------
940
+ /**
941
+ * Delegate to PEAR
942
+ *
943
+ * @param boolean $o
944
+ *
945
+ * @return boolean
946
+ */
947
+ private function isError( $o ) {
948
+ return $o === false;
949
+ }
950
+ /**
951
+ * Initialize an array of table names
952
+ *
953
+ * @param boolean $reload
954
+ */
955
+ private function load_tables( $reload = true ) {
956
+ global $wpdb;
957
+
958
+ if ( $this->_tables_loaded == false || $reload ) {
959
+ $this->_tables = [];
960
+ // clear existing structure
961
+ $query = 'SHOW TABLES';
962
+ $res = $wpdb->get_results( $query, \ARRAY_N );
963
+ // check for errors
964
+ if ( $this->isError( $res ) ) {
965
+ throw new Ruckusing_Exception( \sprintf( "Error executing 'query' with:\n%s\n\nReason: %s\n\n", $query, $wpdb->last_error ), Ruckusing_Exception::QUERY_ERROR );
966
+ }
967
+ foreach ( $res as $row ) {
968
+ $table = $row[0];
969
+ $this->_tables[ $table ] = true;
970
+ }
971
+ }
972
+ }
973
+ /**
974
+ * Check query type
975
+ *
976
+ * @param string $query query to run
977
+ *
978
+ * @return int
979
+ */
980
+ private function determine_query_type( $query ) {
981
+ $query = \strtolower( \trim( $query ) );
982
+ $match = [];
983
+ \preg_match( '/^(\\w)*/i', $query, $match );
984
+ $type = $match[0];
985
+ switch ( $type ) {
986
+ case 'select':
987
+ return SQL_SELECT;
988
+ case 'update':
989
+ return SQL_UPDATE;
990
+ case 'delete':
991
+ return SQL_DELETE;
992
+ case 'insert':
993
+ return SQL_INSERT;
994
+ case 'alter':
995
+ return SQL_ALTER;
996
+ case 'drop':
997
+ return SQL_DROP;
998
+ case 'create':
999
+ return SQL_CREATE;
1000
+ case 'show':
1001
+ return SQL_SHOW;
1002
+ case 'rename':
1003
+ return SQL_RENAME;
1004
+ case 'set':
1005
+ return SQL_SET;
1006
+ default:
1007
+ return SQL_UNKNOWN_QUERY_TYPE;
1008
+ }
1009
+ }
1010
+ /**
1011
+ * Check query type
1012
+ *
1013
+ * @param $query_type
1014
+ * @internal param string $query query to run
1015
+ *
1016
+ * @return boolean
1017
+ */
1018
+ private function is_select( $query_type ) {
1019
+ if ( $query_type == SQL_SELECT ) {
1020
+ return true;
1021
+ }
1022
+ return false;
1023
+ }
1024
+ /**
1025
+ * Detect whether or not the string represents a function call and if so
1026
+ * do not wrap it in single-quotes, otherwise do wrap in single quotes.
1027
+ *
1028
+ * @param string $str
1029
+ *
1030
+ * @return boolean
1031
+ */
1032
+ private function is_sql_method_call( $str ) {
1033
+ $str = \trim( $str );
1034
+ if ( \substr( $str, -2, 2 ) == '()' ) {
1035
+ return true;
1036
+ } else {
1037
+ return false;
1038
+ }
1039
+ }
1040
+ /**
1041
+ * Check if in transaction
1042
+ *
1043
+ * @return boolean
1044
+ */
1045
+ private function inTransaction() {
1046
+ return $this->_in_trx;
1047
+ }
1048
+ /**
1049
+ * Start transaction
1050
+ */
1051
+ private function beginTransaction() {
1052
+ global $wpdb;
1053
+
1054
+ if ( $this->_in_trx === true ) {
1055
+ throw new Ruckusing_Exception( 'Transaction already started', Ruckusing_Exception::QUERY_ERROR );
1056
+ }
1057
+ $wpdb->query( 'START TRANSACTION' );
1058
+ $this->_in_trx = true;
1059
+ }
1060
+ /**
1061
+ * Commit a transaction
1062
+ */
1063
+ private function commit() {
1064
+ global $wpdb;
1065
+
1066
+ if ( $this->_in_trx === false ) {
1067
+ throw new Ruckusing_Exception( 'Transaction not started', Ruckusing_Exception::QUERY_ERROR );
1068
+ }
1069
+ $wpdb->query( 'COMMIT' );
1070
+ $this->_in_trx = false;
1071
+ }
1072
+ /**
1073
+ * Rollback a transaction
1074
+ */
1075
+ private function rollback() {
1076
+ global $wpdb;
1077
+
1078
+ if ( $this->_in_trx === false ) {
1079
+ throw new Ruckusing_Exception( 'Transaction not started', Ruckusing_Exception::QUERY_ERROR );
1080
+ }
1081
+ $wpdb->query( 'ROLLBACK' );
1082
+ $this->_in_trx = false;
1083
+ }
1084
+ }
lib/ruckusing-framework-runner.php ADDED
@@ -0,0 +1,266 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Yoast ORM class.
4
+ *
5
+ * @package Yoast\WP\Lib
6
+ */
7
+
8
+ namespace Yoast\WP\Lib;
9
+
10
+ use YoastSEO_Vendor\Ruckusing_Exception;
11
+ use YoastSEO_Vendor\Ruckusing_FrameworkRunner;
12
+ use YoastSEO_Vendor\Ruckusing_Task_Manager;
13
+ use YoastSEO_Vendor\Ruckusing_Util_Migrator;
14
+
15
+ /**
16
+ * Ruckusing
17
+ *
18
+ * @category Ruckusing
19
+ * @package Ruckusing
20
+ * @author Cody Caughlan <codycaughlan % gmail . com>
21
+ * @link https://github.com/ruckus/ruckusing-migrations
22
+ */
23
+ /**
24
+ * Ruckusing_FrameworkRunner
25
+ *
26
+ * Primary work-horse class. This class bootstraps the framework by loading
27
+ * all adapters and tasks.
28
+ *
29
+ * @category Ruckusing
30
+ * @package Ruckusing
31
+ * @author Cody Caughlan <codycaughlan % gmail . com>
32
+ * @link https://github.com/ruckus/ruckusing-migrations
33
+ */
34
+ class Ruckusing_Framework_Runner extends Ruckusing_FrameworkRunner {
35
+ /**
36
+ * Available DB config (e.g. test,development, production)
37
+ *
38
+ * @var array
39
+ */
40
+ private $_config = [];
41
+ /**
42
+ * Task manager
43
+ *
44
+ * @var Ruckusing_Task_Manager
45
+ */
46
+ private $_task_mgr = null;
47
+ /**
48
+ * Adapter
49
+ *
50
+ * @var Ruckusing_Adapter_Base
51
+ */
52
+ private $_adapter = null;
53
+ /**
54
+ * Current task name
55
+ *
56
+ * @var string
57
+ */
58
+ private $_cur_task_name = '';
59
+ /**
60
+ * Task options
61
+ *
62
+ * @var string
63
+ */
64
+ private $_task_options = '';
65
+ /**
66
+ * Environment
67
+ * default is development
68
+ * but can also be one 'test', 'production', etc...
69
+ *
70
+ * @var string
71
+ */
72
+ private $_env = 'development';
73
+ /**
74
+ * Flag to display help of task
75
+ *
76
+ * @see Ruckusing_FrameworkRunner::parse_args
77
+ *
78
+ * @var boolean
79
+ */
80
+ private $_showhelp = \false;
81
+ /**
82
+ * Creates an instance of Ruckusing_Adapters_Base
83
+ *
84
+ * @param array $config The current config.
85
+ * @param array $argv the supplied command line arguments.
86
+ *
87
+ * @return Ruckusing_FrameworkRunner
88
+ */
89
+ public function __construct( $config, $argv ) {
90
+ \set_error_handler( [ Ruckusing_Exception::class, 'errorHandler' ], \E_ALL );
91
+ \set_exception_handler( [ Ruckusing_Exception::class, 'exceptionHandler' ] );
92
+ // Parse arguments.
93
+ $this->parse_args( $argv );
94
+ // Set config variables.
95
+ $this->_config = $config;
96
+ // Verify config array.
97
+ $this->initialize_db();
98
+ // Initialize tasks.
99
+ $this->init_tasks();
100
+ }
101
+ /**
102
+ * Execute the current task
103
+ */
104
+ public function execute() {
105
+ $output = '';
106
+ if ( empty( $this->_cur_task_name ) ) {
107
+ if ( isset( $_SERVER['argv'][1] ) && \stripos( $_SERVER['argv'][1], '=' ) === \false ) {
108
+ $output .= \sprintf( "\n\tWrong Task format: %s\n", $_SERVER['argv'][1] );
109
+ }
110
+ $output .= $this->help();
111
+ } else {
112
+ if ( $this->_task_mgr->has_task( $this->_cur_task_name ) ) {
113
+ if ( $this->_showhelp ) {
114
+ $output .= $this->_task_mgr->help( $this->_cur_task_name );
115
+ } else {
116
+ $output .= $this->_task_mgr->execute( $this, $this->_cur_task_name, $this->_task_options );
117
+ }
118
+ } else {
119
+ $output .= \sprintf( "\n\tTask not found: %s\n", $this->_cur_task_name );
120
+ $output .= $this->help();
121
+ }
122
+ }
123
+ return $output;
124
+ }
125
+ /**
126
+ * Get the current adapter
127
+ *
128
+ * @return object
129
+ */
130
+ public function get_adapter() {
131
+ return $this->_adapter;
132
+ }
133
+ /**
134
+ * Initialize the task manager
135
+ */
136
+ public function init_tasks() {
137
+ $this->_task_mgr = new Ruckusing_Task_Manager( $this->_adapter, $this->_config );
138
+ }
139
+ /**
140
+ * Get the current migration dir
141
+ *
142
+ * @param string $key the module key name.
143
+ *
144
+ * @return string
145
+ */
146
+ public function migrations_directory( $key = '' ) {
147
+ $migration_dir = '';
148
+ if ( $key ) {
149
+ if ( ! isset( $this->_config['migrations_dir'][ $key ] ) ) {
150
+ throw new Ruckusing_Exception( \sprintf( 'No module %s migration_dir set in config', $key ), Ruckusing_Exception::INVALID_CONFIG );
151
+ }
152
+ $migration_dir = $this->_config['migrations_dir'][ $key ] . \DIRECTORY_SEPARATOR;
153
+ }
154
+ elseif ( \is_array( $this->_config['migrations_dir'] ) ) {
155
+ $migration_dir = $this->_config['migrations_dir']['default'] . \DIRECTORY_SEPARATOR;
156
+ }
157
+ else {
158
+ $migration_dir = $this->_config['migrations_dir'] . \DIRECTORY_SEPARATOR;
159
+ }
160
+ if ( \array_key_exists( 'directory', $this->_config['db'][ $this->_env ] ) ) {
161
+ return $migration_dir . $this->_config['db'][ $this->_env ]['directory'];
162
+ }
163
+ return $migration_dir . $this->_config['db'][ $this->_env ]['database'];
164
+ }
165
+ /**
166
+ * Get all migrations directory
167
+ *
168
+ * @return array
169
+ */
170
+ public function migrations_directories() {
171
+ $result = [];
172
+ if ( \is_array( $this->_config['migrations_dir'] ) ) {
173
+ foreach ( $this->_config['migrations_dir'] as $name => $path ) {
174
+ $result[ $name ] = $path . \DIRECTORY_SEPARATOR;
175
+ }
176
+ } else {
177
+ $result['default'] = $this->_config['migrations_dir'] . \DIRECTORY_SEPARATOR;
178
+ }
179
+ return $result;
180
+ }
181
+ /**
182
+ * Get the current db schema dir
183
+ *
184
+ * @return string
185
+ */
186
+ public function db_directory() {
187
+ $path = $this->_config['db_dir'] . \DIRECTORY_SEPARATOR;
188
+ if ( \array_key_exists( 'directory', $this->_config['db'][ $this->_env ] ) ) {
189
+ return $path . $this->_config['db'][ $this->_env ]['directory'];
190
+ }
191
+ return $path . $this->_config['db'][ $this->_env ]['database'];
192
+ }
193
+ /**
194
+ * Initialize the db
195
+ */
196
+ public function initialize_db() {
197
+ $db = $this->_config['db'][ $this->_env ];
198
+ $this->_adapter = new Ruckusing_Adapter( $db );
199
+ }
200
+ /**
201
+ * $argv is our complete command line argument set.
202
+ * PHP gives us:
203
+ * [0] = the actual file name we're executing
204
+ * [1..N] = all other arguments
205
+ *
206
+ * Our task name should be at slot [1]
207
+ * Anything else are additional parameters that we can pass
208
+ * to our task and they can deal with them as they see fit.
209
+ *
210
+ * @param array $argv the current command line arguments
211
+ */
212
+ private function parse_args( $argv ) {
213
+ $num_args = \count( $argv );
214
+ $options = [];
215
+ for ( $i = 0; $i < $num_args; $i++ ) {
216
+ $arg = $argv[ $i ];
217
+ if ( \stripos( $arg, ':' ) !== \false ) {
218
+ $this->_cur_task_name = $arg;
219
+ } elseif ( $arg == 'help' ) {
220
+ $this->_showhelp = \true;
221
+ continue;
222
+ } elseif ( \stripos( $arg, '=' ) !== \false ) {
223
+ list($key, $value) = \explode( '=', $arg );
224
+ $key = \strtolower( $key );
225
+ // Allow both upper and lower case parameters
226
+ $options[ $key ] = $value;
227
+ if ( $key == 'env' ) {
228
+ $this->_env = $value;
229
+ }
230
+ }
231
+ }
232
+ $this->_task_options = $options;
233
+ }
234
+ /**
235
+ * Update the local schema to handle multiple records versus the prior architecture
236
+ * of storing a single version. In addition take all existing migration files
237
+ * and register them in our new table, as they have already been executed.
238
+ */
239
+ public function update_schema_for_timestamps() {
240
+ // only create the table if it doesnt already exist
241
+ $this->_adapter->create_schema_version_table();
242
+ // insert all existing records into our new table
243
+ $migrator_util = new Ruckusing_Util_Migrator( $this->_adapter );
244
+ $files = $migrator_util->get_migration_files( $this->migrations_directories(), 'up' );
245
+ foreach ( $files as $file ) {
246
+ if ( (int) $file['version'] >= \PHP_INT_MAX ) {
247
+ // its new style like '20081010170207' so its not a candidate
248
+ continue;
249
+ }
250
+ // query old table, if it less than or equal to our max version, then its a candidate for insertion
251
+ $query_sql = \sprintf( 'SELECT version FROM %s WHERE version >= %d', \YoastSEO_Vendor\RUCKUSING_SCHEMA_TBL_NAME, $file['version'] );
252
+ $existing_version_old_style = $this->_adapter->select_one( $query_sql );
253
+ if ( \count( $existing_version_old_style ) > 0 ) {
254
+ // make sure it doesnt exist in our new table, who knows how it got inserted?
255
+ $new_vers_sql = \sprintf( 'SELECT version FROM %s WHERE version = %d', $this->_adapter->get_schema_version_table_name(), $file['version'] );
256
+ $existing_version_new_style = $this->_adapter->select_one( $new_vers_sql );
257
+ if ( empty( $existing_version_new_style ) ) {
258
+ // use sprintf & %d to force it to be stripped of any leading zeros, we *know* this represents an old version style
259
+ // so we dont have to worry about PHP and integer overflow
260
+ $insert_sql = \sprintf( 'INSERT INTO %s (version) VALUES (%d)', $this->_adapter->get_schema_version_table_name(), $file['version'] );
261
+ $this->_adapter->query( $insert_sql );
262
+ }
263
+ }
264
+ }
265
+ }
266
+ }
migrations/20171228151840_WpYoastIndexable.php CHANGED
@@ -5,7 +5,7 @@
5
  * @package WPSEO\Migrations
6
  */
7
 
8
- use Yoast\WP\SEO\ORM\Yoast_Model;
9
  use YoastSEO_Vendor\Ruckusing_Migration_Base;
10
 
11
  /**
@@ -155,6 +155,6 @@ class WpYoastIndexable extends Ruckusing_Migration_Base {
155
  * @return string The table name to use.
156
  */
157
  protected function get_table_name() {
158
- return Yoast_Model::get_table_name( 'Indexable' );
159
  }
160
  }
5
  * @package WPSEO\Migrations
6
  */
7
 
8
+ use Yoast\WP\Lib\Model;
9
  use YoastSEO_Vendor\Ruckusing_Migration_Base;
10
 
11
  /**
155
  * @return string The table name to use.
156
  */
157
  protected function get_table_name() {
158
+ return Model::get_table_name( 'Indexable' );
159
  }
160
  }
migrations/20171228151841_WpYoastPrimaryTerm.php CHANGED
@@ -5,7 +5,7 @@
5
  * @package WPSEO\Migrations
6
  */
7
 
8
- use Yoast\WP\SEO\ORM\Yoast_Model;
9
  use YoastSEO_Vendor\Ruckusing_Migration_Base;
10
 
11
  /**
@@ -91,6 +91,6 @@ class WpYoastPrimaryTerm extends Ruckusing_Migration_Base {
91
  * @return string Table name to use.
92
  */
93
  protected function get_table_name() {
94
- return Yoast_Model::get_table_name( 'Primary_Term' );
95
  }
96
  }
5
  * @package WPSEO\Migrations
6
  */
7
 
8
+ use Yoast\WP\Lib\Model;
9
  use YoastSEO_Vendor\Ruckusing_Migration_Base;
10
 
11
  /**
91
  * @return string Table name to use.
92
  */
93
  protected function get_table_name() {
94
+ return Model::get_table_name( 'Primary_Term' );
95
  }
96
  }
migrations/20190529075038_WpYoastDropIndexableMetaTableIfExists.php CHANGED
@@ -5,7 +5,7 @@
5
  * @package WPSEO\Migrations
6
  */
7
 
8
- use Yoast\WP\SEO\ORM\Yoast_Model;
9
  use YoastSEO_Vendor\Ruckusing_Migration_Base;
10
 
11
  /**
@@ -36,6 +36,6 @@ class WpYoastDropIndexableMetaTableIfExists extends Ruckusing_Migration_Base {
36
  * @return string The table name to use.
37
  */
38
  protected function get_table_name() {
39
- return Yoast_Model::get_table_name( 'Indexable_Meta' );
40
  }
41
  }
5
  * @package WPSEO\Migrations
6
  */
7
 
8
+ use Yoast\WP\Lib\Model;
9
  use YoastSEO_Vendor\Ruckusing_Migration_Base;
10
 
11
  /**
36
  * @return string The table name to use.
37
  */
38
  protected function get_table_name() {
39
+ return Model::get_table_name( 'Indexable_Meta' );
40
  }
41
  }
migrations/20191011111109_WpYoastIndexableHierarchy.php CHANGED
@@ -5,7 +5,7 @@
5
  * @package WPSEO\Migrations
6
  */
7
 
8
- use Yoast\WP\SEO\ORM\Yoast_Model;
9
  use YoastSEO_Vendor\Ruckusing_Migration_Base;
10
 
11
  /**
@@ -53,6 +53,6 @@ class WpYoastIndexableHierarchy extends Ruckusing_Migration_Base {
53
  * @return string The table name to use.
54
  */
55
  protected function get_table_name() {
56
- return Yoast_Model::get_table_name( 'Indexable_Hierarchy' );
57
  }
58
  }
5
  * @package WPSEO\Migrations
6
  */
7
 
8
+ use Yoast\WP\Lib\Model;
9
  use YoastSEO_Vendor\Ruckusing_Migration_Base;
10
 
11
  /**
53
  * @return string The table name to use.
54
  */
55
  protected function get_table_name() {
56
+ return Model::get_table_name( 'Indexable_Hierarchy' );
57
  }
58
  }
migrations/20200408101900_AddCollationToTables.php CHANGED
@@ -5,7 +5,7 @@
5
  * @package WPSEO\Migrations
6
  */
7
 
8
- use Yoast\WP\SEO\ORM\Yoast_Model;
9
  use YoastSEO_Vendor\Ruckusing_Migration_Base;
10
 
11
  /**
@@ -25,10 +25,10 @@ class AddCollationToTables extends Ruckusing_Migration_Base {
25
  }
26
 
27
  $tables = [
28
- Yoast_Model::get_table_name( 'migrations' ),
29
- Yoast_Model::get_table_name( 'Indexable' ),
30
- Yoast_Model::get_table_name( 'Indexable_Hierarchy' ),
31
- Yoast_Model::get_table_name( 'Primary_Term' ),
32
  ];
33
 
34
  foreach ( $tables as $table ) {
5
  * @package WPSEO\Migrations
6
  */
7
 
8
+ use Yoast\WP\Lib\Model;
9
  use YoastSEO_Vendor\Ruckusing_Migration_Base;
10
 
11
  /**
25
  }
26
 
27
  $tables = [
28
+ Model::get_table_name( 'migrations' ),
29
+ Model::get_table_name( 'Indexable' ),
30
+ Model::get_table_name( 'Indexable_Hierarchy' ),
31
+ Model::get_table_name( 'Primary_Term' ),
32
  ];
33
 
34
  foreach ( $tables as $table ) {
migrations/20200420073606_AddColumnsToIndexables.php CHANGED
@@ -5,7 +5,7 @@
5
  * @package WPSEO\Migrations
6
  */
7
 
8
- use Yoast\WP\SEO\ORM\Yoast_Model;
9
  use YoastSEO_Vendor\Ruckusing_Migration_Base;
10
 
11
  /**
@@ -56,7 +56,7 @@ class AddColumnsToIndexables extends Ruckusing_Migration_Base {
56
  * @return string The Indexable table name.
57
  */
58
  protected function get_indexable_table() {
59
- return Yoast_Model::get_table_name( 'Indexable' );
60
  }
61
 
62
  /**
@@ -67,8 +67,8 @@ class AddColumnsToIndexables extends Ruckusing_Migration_Base {
67
  protected function get_tables() {
68
  return [
69
  $this->get_indexable_table(),
70
- Yoast_Model::get_table_name( 'Indexable_Hierarchy' ),
71
- Yoast_Model::get_table_name( 'Primary_Term' ),
72
  ];
73
  }
74
  }
5
  * @package WPSEO\Migrations
6
  */
7
 
8
+ use Yoast\WP\Lib\Model;
9
  use YoastSEO_Vendor\Ruckusing_Migration_Base;
10
 
11
  /**
56
  * @return string The Indexable table name.
57
  */
58
  protected function get_indexable_table() {
59
+ return Model::get_table_name( 'Indexable' );
60
  }
61
 
62
  /**
67
  protected function get_tables() {
68
  return [
69
  $this->get_indexable_table(),
70
+ Model::get_table_name( 'Indexable_Hierarchy' ),
71
+ Model::get_table_name( 'Primary_Term' ),
72
  ];
73
  }
74
  }
migrations/20200428123747_BreadcrumbTitleAndHierarchyReset.php CHANGED
@@ -5,7 +5,7 @@
5
  * @package WPSEO\Migrations
6
  */
7
 
8
- use Yoast\WP\SEO\ORM\Yoast_Model;
9
  use YoastSEO_Vendor\Ruckusing_Migration_Base;
10
 
11
  /**
@@ -38,7 +38,7 @@ class BreadcrumbTitleAndHierarchyReset extends Ruckusing_Migration_Base {
38
  * @return string The table name to use.
39
  */
40
  protected function get_indexable_table_name() {
41
- return Yoast_Model::get_table_name( 'Indexable' );
42
  }
43
 
44
  /**
@@ -47,6 +47,6 @@ class BreadcrumbTitleAndHierarchyReset extends Ruckusing_Migration_Base {
47
  * @return string The table name to use.
48
  */
49
  protected function get_indexable_hierarchy_table_name() {
50
- return Yoast_Model::get_table_name( 'Indexable_Hierarchy' );
51
  }
52
  }
5
  * @package WPSEO\Migrations
6
  */
7
 
8
+ use Yoast\WP\Lib\Model;
9
  use YoastSEO_Vendor\Ruckusing_Migration_Base;
10
 
11
  /**
38
  * @return string The table name to use.
39
  */
40
  protected function get_indexable_table_name() {
41
+ return Model::get_table_name( 'Indexable' );
42
  }
43
 
44
  /**
47
  * @return string The table name to use.
48
  */
49
  protected function get_indexable_hierarchy_table_name() {
50
+ return Model::get_table_name( 'Indexable_Hierarchy' );
51
  }
52
  }
migrations/20200428194858_ExpandIndexableColumnLengths.php ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Yoast SEO Plugin File.
4
+ *
5
+ * @package WPSEO\Migrations
6
+ */
7
+
8
+ use Yoast\WP\Lib\Model;
9
+ use YoastSEO_Vendor\Ruckusing_Migration_Base;
10
+
11
+ /**
12
+ * ExpandIndexableColumnLengths
13
+ */
14
+ class ExpandIndexableColumnLengths extends Ruckusing_Migration_Base {
15
+
16
+ /**
17
+ * Migration up.
18
+ */
19
+ public function up() {
20
+ $this->change_column( $this->get_table_name(), 'title', 'text', [ 'null' => true ] );
21
+ $this->change_column( $this->get_table_name(), 'open_graph_title', 'text', [ 'null' => true ] );
22
+ $this->change_column( $this->get_table_name(), 'twitter_title', 'text', [ 'null' => true ] );
23
+ $this->change_column( $this->get_table_name(), 'open_graph_image_source', 'text', [ 'null' => true ] );
24
+ $this->change_column( $this->get_table_name(), 'twitter_image_source', 'text', [ 'null' => true ] );
25
+ }
26
+
27
+ /**
28
+ * Migration down.
29
+ */
30
+ public function down() {
31
+ $this->change_column(
32
+ $this->get_table_name(),
33
+ 'title',
34
+ 'string',
35
+ [ 'null' => true, 'limit' => 191 ]
36
+ );
37
+ $this->change_column(
38
+ $this->get_table_name(),
39
+ 'opengraph_title',
40
+ 'string',
41
+ [ 'null' => true, 'limit' => 191 ]
42
+ );
43
+ $this->change_column(
44
+ $this->get_table_name(),
45
+ 'twitter_title',
46
+ 'string',
47
+ [ 'null' => true, 'limit' => 191 ]
48
+ );
49
+ $this->change_column(
50
+ $this->get_table_name(),
51
+ 'open_graph_image_source',
52
+ 'string',
53
+ [ 'null' => true, 'limit' => 191 ]
54
+ );
55
+ $this->change_column(
56
+ $this->get_table_name(),
57
+ 'twitter_image_source',
58
+ 'string',
59
+ [ 'null' => true, 'limit' => 191 ]
60
+ );
61
+ }
62
+
63
+ /**
64
+ * Retrieves the table name to use for storing indexables.
65
+ *
66
+ * @return string The table name to use.
67
+ */
68
+ protected function get_table_name() {
69
+ return Model::get_table_name( 'Indexable' );
70
+ }
71
+ }
migrations/20200429105310_TruncateIndexableTables.php ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Yoast SEO Plugin File.
4
+ *
5
+ * @package WPSEO\Migrations
6
+ */
7
+
8
+ use Yoast\WP\Lib\Model;
9
+ use YoastSEO_Vendor\Ruckusing_Migration_Base;
10
+
11
+ /**
12
+ * TruncateIndexableTables
13
+ */
14
+ class TruncateIndexableTables extends Ruckusing_Migration_Base {
15
+
16
+ /**
17
+ * Migration up.
18
+ */
19
+ public function up() {
20
+ $this->query( 'TRUNCATE TABLE ' . $this->get_indexable_table_name() );
21
+ $this->query( 'TRUNCATE TABLE ' . $this->get_indexable_hierarchy_table_name() );
22
+ }
23
+
24
+ /**
25
+ * Migration down.
26
+ */
27
+ public function down() {
28
+ // Nothing to do.
29
+ }
30
+
31
+ /**
32
+ * Retrieves the table name to use for storing indexables.
33
+ *
34
+ * @return string The table name to use.
35
+ */
36
+ protected function get_indexable_table_name() {
37
+ return Model::get_table_name( 'Indexable' );
38
+ }
39
+
40
+ /**
41
+ * Retrieves the table name to use.
42
+ *
43
+ * @return string The table name to use.
44
+ */
45
+ protected function get_indexable_hierarchy_table_name() {
46
+ return Model::get_table_name( 'Indexable_Hierarchy' );
47
+ }
48
+ }
polyfills/pdo/pdo-mysqli-polyfill.php DELETED
@@ -1,297 +0,0 @@
1
- <?php
2
- /**
3
- * These are polyfills of PDO in case the extension isn't loaded.
4
- *
5
- * This code is primarily based on the MySQLi driver for Doctrine.
6
- *
7
- * The Doctrine license is included below:
8
- *
9
- * Copyright (c) 2006-2018 Doctrine Project
10
- * Permission is hereby granted, free of charge, to any person obtaining a copy of
11
- * this software and associated documentation files (the "Software"), to deal in
12
- * the Software without restriction, including without limitation the rights to
13
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
14
- * of the Software, and to permit persons to whom the Software is furnished to do
15
- * so, subject to the following conditions:
16
- *
17
- * The above copyright notice and this permission notice shall be included in all
18
- * copies or substantial portions of the Software.
19
- *
20
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
- * SOFTWARE.
27
- */
28
-
29
- namespace Yoast\WP\Polyfills\PDO;
30
-
31
- class PDO_MySQLi_Polyfill {
32
- /**
33
- * Name of the option to set connection flags
34
- */
35
- const OPTION_FLAGS = 'flags';
36
-
37
- /** @var mysqli */
38
- private $conn;
39
-
40
- /**
41
- * @param array<string, mixed> $params The params.
42
- * @param array<int, mixed> $driverOptions The driver options.
43
- *
44
- * @throws \Exception
45
- */
46
- public function __construct( $params, $username, $password, $driverOptions = [] ) {
47
- if ( is_string( $params ) ) {
48
- $config = substr( $params, 6 );
49
- $parts = explode( ';', $config );
50
- $params = [];
51
- foreach ( $parts as $part ) {
52
- if ( strpos( $part, '=' ) === false ) {
53
- continue;
54
- }
55
- list( $key, $value ) = explode( '=', $part );
56
- $params[ $key ] = $value;
57
- }
58
- }
59
-
60
-
61
- $port = isset( $params['port'] ) ? $params['port'] : (int) ini_get( 'mysqli.default_port' );
62
-
63
- // Fallback to default MySQL port if not given.
64
- if ( ! $port ) {
65
- $port = 3306;
66
- }
67
-
68
- $socket = isset( $params['unix_socket'] ) ? $params['unix_socket'] : ini_get( 'mysqli.default_socket' );
69
- $dbname = isset( $params['dbname'] ) ? $params['dbname'] : '';
70
- $host = $params['host'];
71
-
72
- if ( ! empty( $params['persistent'] ) ) {
73
- $host = 'p:' . $host;
74
- }
75
-
76
- $flags = isset( $driverOptions[ static::OPTION_FLAGS ] ) ? $driverOptions[ static::OPTION_FLAGS ] : 0;
77
-
78
- $this->conn = mysqli_init();
79
-
80
- $this->setSecureConnection( $params );
81
- $this->setDriverOptions( $driverOptions );
82
-
83
- set_error_handler(static function () {
84
- return true;
85
- });
86
- try {
87
- if ( ! $this->conn->real_connect( $host, $username, $password, $dbname, $port, $socket, $flags ) ) {
88
- throw new \Exception( $this->conn->error, $this->conn->errno );
89
- }
90
- } finally {
91
- restore_error_handler();
92
- }
93
-
94
- if ( ! isset( $params['charset'] ) ) {
95
- return;
96
- }
97
-
98
- $this->conn->set_charset( $params['charset'] );
99
- }
100
-
101
- /**
102
- * Retrieves mysqli native resource handle.
103
- *
104
- * Could be used if part of your application is not using DBAL.
105
- */
106
- public function getWrappedResourceHandle() {
107
- return $this->conn;
108
- }
109
-
110
- /**
111
- * @inheritdoc
112
- *
113
- * The server version detection includes a special case for MariaDB
114
- * to support '5.5.5-' prefixed versions introduced in Maria 10+
115
- *
116
- * @link https://jira.mariadb.org/browse/MDEV-4088
117
- */
118
- public function getServerVersion() {
119
- $serverInfos = $this->conn->get_server_info();
120
- if ( stripos( $serverInfos, 'mariadb' ) !== false ) {
121
- return $serverInfos;
122
- }
123
-
124
- $majorVersion = floor( $this->conn->server_version / 10000 );
125
- $minorVersion = floor( ($this->conn->server_version - $majorVersion * 10000) / 100 );
126
- $patchVersion = floor( $this->conn->server_version - $majorVersion * 10000 - $minorVersion * 100 );
127
-
128
- return $majorVersion . '.' . $minorVersion . '.' . $patchVersion;
129
- }
130
-
131
- /**
132
- * @inheritDoc
133
- */
134
- public function prepare( $sql ) {
135
- return new PDO_MySQLi_Statement_Polyfill( $this->conn, $sql );
136
- }
137
-
138
- /**
139
- * @inheritDoc
140
- */
141
- public function query( $sql ) {
142
- $stmt = $this->prepare( $sql );
143
- $stmt->execute();
144
-
145
- return $stmt;
146
- }
147
-
148
- /**
149
- * @inheritDoc
150
- */
151
- public function quote( $input ) {
152
- return "'" . $this->conn->escape_string( $input ) . "'";
153
- }
154
-
155
- /**
156
- * @inheritDoc
157
- */
158
- public function exec( $statement ) {
159
- if ( $this->conn->query( $statement ) === false ) {
160
- throw new \Exception( $this->conn->error, $this->conn->errno );
161
- }
162
-
163
- return $this->conn->affected_rows;
164
- }
165
-
166
- /**
167
- * @inheritDoc
168
- */
169
- public function lastInsertId( $name = null ) {
170
- return (string) $this->conn->insert_id;
171
- }
172
-
173
- /**
174
- * @inheritDoc
175
- */
176
- public function beginTransaction() {
177
- $this->conn->query( 'START TRANSACTION' );
178
- }
179
-
180
- /**
181
- * @inheritDoc
182
- */
183
- public function commit() {
184
- if ( ! $this->conn->commit() ) {
185
- throw new \Exception( $this->conn->error, $this->conn->errno );
186
- }
187
- }
188
-
189
- /**
190
- * @inheritDoc
191
- */
192
- public function rollBack() {
193
- if ( ! $this->conn->rollback() ) {
194
- throw new \Exception( $this->conn->error, $this->conn->errno );
195
- }
196
- }
197
-
198
- /**
199
- * Apply the driver options to the connection.
200
- *
201
- * @param array<int, mixed> $driverOptions
202
- *
203
- * @throws \Exception When one of of the options is not supported.
204
- * @throws \Exception When applying doesn't work - e.g. due to incorrect value.
205
- */
206
- private function setDriverOptions( $driverOptions = [] ) {
207
- if ( empty( $driverOptions ) ) {
208
- return;
209
- }
210
-
211
- $supportedDriverOptions = [
212
- MYSQLI_OPT_CONNECT_TIMEOUT,
213
- MYSQLI_OPT_LOCAL_INFILE,
214
- MYSQLI_INIT_COMMAND,
215
- MYSQLI_READ_DEFAULT_FILE,
216
- MYSQLI_READ_DEFAULT_GROUP,
217
- ];
218
-
219
- if ( defined( 'MYSQLI_SERVER_PUBLIC_KEY' ) ) {
220
- $supportedDriverOptions[] = MYSQLI_SERVER_PUBLIC_KEY;
221
- }
222
-
223
- $exceptionMsg = "%s option '%s' with value '%s'";
224
-
225
- foreach ( $driverOptions as $option => $value ) {
226
- if ( $option === static::OPTION_FLAGS ) {
227
- continue;
228
- }
229
-
230
- if ( ! in_array( $option, $supportedDriverOptions, true ) ) {
231
- throw new \Exception(
232
- sprintf( $exceptionMsg, 'Unsupported', $option, $value )
233
- );
234
- }
235
-
236
- if ( @mysqli_options( $this->conn, $option, $value ) ) {
237
- continue;
238
- }
239
-
240
- throw new \Exception( $this->conn->error, $this->conn->errno );
241
- }
242
- }
243
-
244
- /**
245
- * Pings the server and re-connects when `mysqli.reconnect = 1`
246
- *
247
- * @inheritDoc
248
- */
249
- public function ping() {
250
- if ( ! $this->conn->ping() ) {
251
- throw new \Exception( $this->conn->error, $this->conn->errno );
252
- }
253
- }
254
-
255
- /**
256
- * @inheritDoc
257
- */
258
- public function getAttribute( $name ) {
259
- if ( $name === \PDO::ATTR_DRIVER_NAME ) {
260
- return 'mysql';
261
- }
262
- return null;
263
- }
264
-
265
- /**
266
- * @inheritDoc
267
- */
268
- public function setAttribute( $attribute, $value ) {
269
- return false;
270
- }
271
-
272
- /**
273
- * Establish a secure connection
274
- *
275
- * @param array<string, mixed> $params
276
- *
277
- * @throws \Exception
278
- */
279
- private function setSecureConnection( $params ) {
280
- if ( ! isset( $params['ssl_key'] ) &&
281
- ! isset( $params['ssl_cert'] ) &&
282
- ! isset( $params['ssl_ca'] ) &&
283
- ! isset( $params['ssl_capath'] ) &&
284
- ! isset( $params['ssl_cipher'] )
285
- ) {
286
- return;
287
- }
288
-
289
- $this->conn->ssl_set(
290
- isset( $params['ssl_key'] ) ? $params['ssl_key'] : null,
291
- isset( $params['ssl_cert'] ) ? $params['ssl_cert'] : null,
292
- isset( $params['ssl_ca'] ) ? $params['ssl_ca'] : null,
293
- isset( $params['ssl_capath'] ) ? $params['ssl_capath'] : null,
294
- isset( $params['ssl_cipher'] ) ? $params['ssl_cipher'] : null
295
- );
296
- }
297
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
polyfills/pdo/pdo-mysqli-statement-polyfill.php DELETED
@@ -1,448 +0,0 @@
1
- <?php
2
- /**
3
- * These are polyfills of PDO in case the extension isn't loaded.
4
- *
5
- * This code is primarily based on the MySQLi driver for Doctrine.
6
- *
7
- * The Doctrine license is included below:
8
- *
9
- * Copyright (c) 2006-2018 Doctrine Project
10
- * Permission is hereby granted, free of charge, to any person obtaining a copy of
11
- * this software and associated documentation files (the "Software"), to deal in
12
- * the Software without restriction, including without limitation the rights to
13
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
14
- * of the Software, and to permit persons to whom the Software is furnished to do
15
- * so, subject to the following conditions:
16
- *
17
- * The above copyright notice and this permission notice shall be included in all
18
- * copies or substantial portions of the Software.
19
- *
20
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
- * SOFTWARE.
27
- */
28
-
29
- namespace Yoast\WP\Polyfills\PDO;
30
-
31
- final class PDO_MySQLi_Statement_Polyfill implements \IteratorAggregate {
32
-
33
- /** @var string[] */
34
- private static $paramTypeMap = [
35
- \PDO::PARAM_STR => 's',
36
- 16 => 's',
37
- \PDO::PARAM_BOOL => 'i',
38
- \PDO::PARAM_NULL => 's',
39
- \PDO::PARAM_INT => 'i',
40
- \PDO::PARAM_LOB => 'b',
41
- ];
42
-
43
- /** @var mysqli */
44
- private $conn;
45
-
46
- /** @var mysqli_stmt */
47
- private $stmt;
48
-
49
- /**
50
- * Whether the statement result metadata has been fetched.
51
- *
52
- * @var bool
53
- */
54
- private $metadataFetched = false;
55
-
56
- /**
57
- * Whether the statement result has columns. The property should be used only after the result metadata
58
- * has been fetched ({@see $metadataFetched}). Otherwise, the property value is undetermined.
59
- *
60
- * @var bool
61
- */
62
- private $hasColumns = false;
63
-
64
- /**
65
- * Mapping of statement result column indexes to their names. The property should be used only
66
- * if the statement result has columns ({@see $hasColumns}). Otherwise, the property value is undetermined.
67
- *
68
- * @var array<int,string>
69
- */
70
- private $columnNames = [];
71
-
72
- /** @var mixed[] */
73
- private $rowBoundValues = [];
74
-
75
- /** @var mixed[] */
76
- private $boundValues = [];
77
-
78
- /** @var string */
79
- private $types;
80
-
81
- /**
82
- * Contains ref values for bindValue().
83
- *
84
- * @var mixed[]
85
- */
86
- private $values = [];
87
-
88
- /** @var int */
89
- private $defaultFetchMode = \PDO::FETCH_BOTH;
90
-
91
- /**
92
- * Indicates whether the statement is in the state when fetching results is possible
93
- *
94
- * @var bool
95
- */
96
- private $result = false;
97
-
98
- /**
99
- * @throws \Exception
100
- */
101
- public function __construct( $conn, $sql ) {
102
- $this->conn = $conn;
103
-
104
- $stmt = $conn->prepare( $sql );
105
-
106
- if ( $stmt === false ) {
107
- throw new \Exception( $this->conn->error, $this->conn->errno );
108
- }
109
-
110
- $this->stmt = $stmt;
111
-
112
- $paramCount = $this->stmt->param_count;
113
- if ( 0 >= $paramCount ) {
114
- return;
115
- }
116
-
117
- $this->types = str_repeat( 's', $paramCount );
118
- $this->boundValues = array_fill( 1, $paramCount, null );
119
- }
120
-
121
- /**
122
- * @inheritdoc
123
- */
124
- public function bindParam( $param, &$variable, $type = \PDO::PARAM_STR, $length = null ) {
125
- assert( is_int( $param ) );
126
-
127
- if ( ! isset( self::$paramTypeMap[ $type ] ) ) {
128
- throw new \Exception( sprintf( 'Unknown type, %d given.', $type ) );
129
- }
130
-
131
- $this->boundValues[ $param ] =& $variable;
132
- $this->types[ ($param - 1) ] = self::$paramTypeMap[ $type ];
133
- }
134
-
135
- /**
136
- * @inheritdoc
137
- */
138
- public function bindValue( $param, $value, $type = \PDO::PARAM_STR ) {
139
- assert( is_int( $param ) );
140
-
141
- if ( ! isset( self::$paramTypeMap[ $type ] ) ) {
142
- throw new \Exception( sprintf( 'Unknown type, %d given.', $type ) );
143
- }
144
-
145
- $this->values[ $param ] = $value;
146
- $this->boundValues[ $param ] =& $this->values[ $param ];
147
- $this->types[ ($param - 1) ] = self::$paramTypeMap[ $type ];
148
- }
149
-
150
- /**
151
- * @inheritdoc
152
- */
153
- public function execute( $params = null ) {
154
- if ( $params !== null && count( $params ) > 0 ) {
155
- if ( ! $this->bindUntypedValues( $params ) ) {
156
- throw new \Exception( $this->stmt->error, $this->stmt->errno );
157
- }
158
- } else {
159
- $this->bindTypedParameters();
160
- }
161
-
162
- if ( ! $this->stmt->execute() ) {
163
- throw new \Exception( $this->stmt->error, $this->stmt->errno );
164
- }
165
-
166
- if ( ! $this->metadataFetched ) {
167
- $meta = $this->stmt->result_metadata();
168
- if ( $meta !== false ) {
169
- $this->hasColumns = true;
170
-
171
- $fields = $meta->fetch_fields();
172
- assert( is_array( $fields ) );
173
-
174
- $this->columnNames = array_map(static function ( \stdClass $field ) {
175
- return $field->name;
176
- }, $fields);
177
-
178
- $meta->free();
179
- } else {
180
- $this->hasColumns = false;
181
- }
182
-
183
- $this->metadataFetched = true;
184
- }
185
-
186
- if ( $this->hasColumns ) {
187
- // Store result of every execution which has it. Otherwise it will be impossible
188
- // to execute a new statement in case if the previous one has non-fetched rows
189
- // @link http://dev.mysql.com/doc/refman/5.7/en/commands-out-of-sync.html
190
- $this->stmt->store_result();
191
-
192
- // Bind row values _after_ storing the result. Otherwise, if mysqli is compiled with libmysql,
193
- // it will have to allocate as much memory as it may be needed for the given column type
194
- // (e.g. for a LONGBLOB field it's 4 gigabytes)
195
- // @link https://bugs.php.net/bug.php?id=51386#1270673122
196
- //
197
- // Make sure that the values are bound after each execution. Otherwise, if closeCursor() has been
198
- // previously called on the statement, the values are unbound making the statement unusable.
199
- //
200
- // It's also important that row values are bound after _each_ call to store_result(). Otherwise,
201
- // if mysqli is compiled with libmysql, subsequently fetched string values will get truncated
202
- // to the length of the ones fetched during the previous execution.
203
- $this->rowBoundValues = array_fill( 0, count( $this->columnNames ), null );
204
-
205
- $refs = [];
206
- foreach ( $this->rowBoundValues as $key => &$value ) {
207
- $refs[ $key ] =& $value;
208
- }
209
-
210
- if ( ! $this->stmt->bind_result( ...$refs ) ) {
211
- throw new \Exception( $this->stmt->error, $this->stmt->errno );
212
- }
213
- }
214
-
215
- $this->result = true;
216
- }
217
-
218
- /**
219
- * Binds parameters with known types previously bound to the statement
220
- *
221
- * @throws \Exception
222
- */
223
- private function bindTypedParameters() {
224
- $streams = $values = [];
225
- $types = $this->types;
226
-
227
- foreach ( $this->boundValues as $parameter => $value ) {
228
- assert( is_int( $parameter ) );
229
- if ( ! isset( $types[ ($parameter - 1) ] ) ) {
230
- $types[ ($parameter - 1) ] = self::$paramTypeMap[ \PDO::PARAM_STR ];
231
- }
232
-
233
- if ( $types[ ($parameter - 1) ] === self::$paramTypeMap[ \PDO::PARAM_LOB ] ) {
234
- if ( is_resource( $value ) ) {
235
- if ( get_resource_type( $value ) !== 'stream' ) {
236
- throw new \Exception( 'Resources passed with the LARGE_OBJECT parameter type must be stream resources.' );
237
- }
238
-
239
- $streams[ $parameter ] = $value;
240
- $values[ $parameter ] = null;
241
- continue;
242
- }
243
-
244
- $types[ ($parameter - 1) ] = self::$paramTypeMap[ \PDO::PARAM_STR ];
245
- }
246
-
247
- $values[ $parameter ] = $value;
248
- }
249
-
250
- if ( count( $values ) > 0 && ! $this->stmt->bind_param( $types, ...$values ) ) {
251
- throw new \Exception( $this->stmt->error, $this->stmt->errno );
252
- }
253
-
254
- $this->sendLongData( $streams );
255
- }
256
-
257
- /**
258
- * Handle $this->_longData after regular query parameters have been bound
259
- *
260
- * @param array<int, resource> $streams
261
- *
262
- * @throws \Exception
263
- */
264
- private function sendLongData( $streams ) {
265
- foreach ( $streams as $paramNr => $stream ) {
266
- while ( ! feof( $stream ) ) {
267
- $chunk = fread( $stream, 8192 );
268
-
269
- if ( $chunk === false ) {
270
- return new \Exception( sprintf( 'Failed reading the stream resource for parameter offset %d.', $paramNr ) );
271
- }
272
-
273
- if ( ! $this->stmt->send_long_data( ($paramNr - 1), $chunk ) ) {
274
- throw new \Exception( $this->stmt->error, $this->stmt->errno );
275
- }
276
- }
277
- }
278
- }
279
-
280
- /**
281
- * Binds a array of values to bound parameters.
282
- *
283
- * @param mixed[] $values
284
- */
285
- private function bindUntypedValues( $values ) {
286
- $params = [];
287
- $types = str_repeat( 's', count( $values ) );
288
-
289
- foreach ( $values as &$v ) {
290
- $params[] =& $v;
291
- }
292
-
293
- return $this->stmt->bind_param( $types, ...$params );
294
- }
295
-
296
- /**
297
- * @return mixed[]|false|null
298
- */
299
- private function _fetch() {
300
- $ret = $this->stmt->fetch();
301
-
302
- if ( $ret === true ) {
303
- $values = [];
304
- foreach ( $this->rowBoundValues as $v ) {
305
- $values[] = $v;
306
- }
307
-
308
- return $values;
309
- }
310
-
311
- return $ret;
312
- }
313
-
314
- /**
315
- * @inheritdoc
316
- */
317
- public function fetch( $fetchMode = null, ...$args ) {
318
- // do not try fetching from the statement if it's not expected to contain result
319
- // in order to prevent exceptional situation
320
- if ( ! $this->result ) {
321
- return false;
322
- }
323
-
324
- $fetchMode = $fetchMode ?: $this->defaultFetchMode;
325
-
326
- if ( $fetchMode === \PDO::FETCH_COLUMN ) {
327
- return $this->fetchColumn();
328
- }
329
-
330
- $values = $this->_fetch();
331
-
332
- if ( $values === null ) {
333
- return false;
334
- }
335
-
336
- if ( $values === false ) {
337
- throw new \Exception( $this->stmt->error, $this->stmt->errno );
338
- }
339
-
340
- if ( $fetchMode === \PDO::FETCH_NUM ) {
341
- return $values;
342
- }
343
-
344
- $assoc = array_combine( $this->columnNames, $values );
345
- assert( is_array( $assoc ) );
346
-
347
- switch ( $fetchMode ) {
348
- case \PDO::FETCH_ASSOC:
349
- return $assoc;
350
-
351
- case \PDO::FETCH_BOTH:
352
- return ($assoc + $values);
353
-
354
- case \PDO::FETCH_OBJ:
355
- return (object) $assoc;
356
-
357
- default:
358
- throw new \Exception( sprintf( 'Unknown fetch mode %d.', $fetchMode ) );
359
- }
360
- }
361
-
362
- /**
363
- * @inheritdoc
364
- */
365
- public function fetchAll( $fetchMode = null, ...$args ) {
366
- $fetchMode = $fetchMode ?: $this->defaultFetchMode;
367
-
368
- $rows = [];
369
-
370
- if ( $fetchMode === \PDO::FETCH_COLUMN ) {
371
- while ( ($row = $this->fetchColumn()) !== false ) {
372
- $rows[] = $row;
373
- }
374
- } else {
375
- while ( ($row = $this->fetch( $fetchMode )) !== false ) {
376
- $rows[] = $row;
377
- }
378
- }
379
-
380
- return $rows;
381
- }
382
-
383
- /**
384
- * @inheritdoc
385
- */
386
- public function fetchColumn( $columnIndex = 0 ) {
387
- $row = $this->fetch( \PDO::FETCH_NUM );
388
-
389
- if ( $row === false ) {
390
- return false;
391
- }
392
-
393
- if ( ! array_key_exists( $columnIndex, $row ) ) {
394
- $count = count( $row );
395
- throw new \Exception( sprintf(
396
- 'Invalid column index %d. The statement result contains %d column%s.',
397
- $columnIndex,
398
- $count,
399
- $count === 1 ? '' : 's'
400
- ) );
401
- }
402
-
403
- return $row[ $columnIndex ];
404
- }
405
-
406
-
407
- /**
408
- * @inheritDoc
409
- */
410
- public function closeCursor() {
411
- $this->stmt->free_result();
412
- $this->result = false;
413
- }
414
-
415
- /**
416
- * @inheritDoc
417
- */
418
- public function rowCount() {
419
- if ( $this->hasColumns ) {
420
- return $this->stmt->num_rows;
421
- }
422
-
423
- return $this->stmt->affected_rows;
424
- }
425
-
426
- /**
427
- * @inheritDoc
428
- */
429
- public function columnCount() {
430
- return $this->stmt->field_count;
431
- }
432
-
433
- /**
434
- * @inheritdoc
435
- */
436
- public function setFetchMode( $fetchMode, ...$args ) {
437
- $this->defaultFetchMode = $fetchMode;
438
- }
439
-
440
- /**
441
- * @inheritdoc
442
- */
443
- public function getIterator() {
444
- while ( ($result = $this->fetch()) !== false ) {
445
- yield $result;
446
- }
447
- }
448
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
readme.txt CHANGED
@@ -6,7 +6,7 @@ License URI: http://www.gnu.org/licenses/gpl.html
6
  Tags: SEO, XML sitemap, Content analysis, Readability
7
  Requires at least: 5.3
8
  Tested up to: 5.4
9
- Stable tag: 14.0.1
10
  Requires PHP: 5.6.20
11
 
12
  Improve your WordPress SEO: Write better content and have a fully optimized WordPress site using the Yoast SEO plugin.
@@ -209,6 +209,28 @@ Your question has most likely been answered on our knowledge base: [kb.yoast.com
209
 
210
  == Changelog ==
211
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
212
  = 14.0.1 =
213
  Release Date: April 28th, 2020
214
 
6
  Tags: SEO, XML sitemap, Content analysis, Readability
7
  Requires at least: 5.3
8
  Tested up to: 5.4
9
+ Stable tag: 14.0.2
10
  Requires PHP: 5.6.20
11
 
12
  Improve your WordPress SEO: Write better content and have a fully optimized WordPress site using the Yoast SEO plugin.
209
 
210
  == Changelog ==
211
 
212
+ = 14.0.2 =
213
+ Release Date: April 29th, 2020
214
+
215
+ Because we’ve changed the underlying framework of our Indexables technology, we’ve chosen to rebuild the table. This means you will have to go through the indexing process again. We’re sorry. For sites with more than 10,000 posts, we advise using the [WP CLI command to do the indexation on the server](https://yoa.st/wp-cli-index).
216
+
217
+ Bugfixes:
218
+
219
+ * Fixes a bug where a fatal error would be thrown when a title contained more than 191 characters.
220
+ * Fixes a bug where a fatal error would be thrown when a focus keyphrase contained more than 191 characters.
221
+ * Fixes a bug where a fatal error would be thrown when search engines were disallowed from indexing the site.
222
+ * Fixes a bug where a fatal error would be thrown on WooCommerce installations when the `wpseo_metadesc` filter was called with only 1 argument.
223
+ * Fixes a bug where a fatal error would be thrown when using the `WPSEO_Frontend` class to get the meta description.
224
+ * Fixes a bug where a fatal error would be thrown when `WPSEO_Frontend` or `WPSEO_Breadcrumbs` was called before the `init` action.
225
+ * Fixes a bug where a non-object property retrieval notice would be thrown when the site's content was being indexed.
226
+ * Fixes a bug where a trailing slash would be added to canonical URLs and some `rel="prev"` URLs, even when the permalink structure settings didn't contain that trailing slash.
227
+ * Fixes a bug where a double breadcrumb would be shown on home pages.
228
+ * Fixes a bug where the indexation would continue indefinitely under specific circumstances.
229
+
230
+ Other:
231
+
232
+ * Removes all usages of `PDO` and `mysqli` directly and uses `wpdb` everywhere. This should prevent a lot of errors for database installations that have different encodings or configurations than what is generally seen.
233
+
234
  = 14.0.1 =
235
  Release Date: April 28th, 2020
236
 
src/actions/indexation/indexable-post-indexation-action.php CHANGED
@@ -11,7 +11,7 @@ use wpdb;
11
  use Yoast\WP\SEO\Builders\Indexable_Builder;
12
  use Yoast\WP\SEO\Helpers\Post_Type_Helper;
13
  use Yoast\WP\SEO\Models\Indexable;
14
- use Yoast\WP\SEO\ORM\Yoast_Model;
15
 
16
  /**
17
  * Indexable_Post_Indexation_Action class.
@@ -79,10 +79,7 @@ class Indexable_Post_Indexation_Action implements Indexation_Action_Interface {
79
 
80
  $indexables = [];
81
  foreach ( $post_ids as $post_id ) {
82
- $indexable = $this->builder->build_for_id_and_type( (int) $post_id, 'post' );
83
- if ( $indexable !== false ) {
84
- $indexables[] = $indexable;
85
- }
86
  }
87
 
88
  return $indexables;
@@ -117,7 +114,7 @@ class Indexable_Post_Indexation_Action implements Indexation_Action_Interface {
117
  protected function get_query( $count, $limit = 1 ) {
118
  $public_post_types = $this->post_type_helper->get_public_post_types();
119
  $placeholders = \implode( ', ', \array_fill( 0, \count( $public_post_types ), '%s' ) );
120
- $indexable_table = Yoast_Model::get_table_name( 'Indexable' );
121
  $replacements = $public_post_types;
122
 
123
  $select = 'ID';
11
  use Yoast\WP\SEO\Builders\Indexable_Builder;
12
  use Yoast\WP\SEO\Helpers\Post_Type_Helper;
13
  use Yoast\WP\SEO\Models\Indexable;
14
+ use Yoast\WP\Lib\Model;
15
 
16
  /**
17
  * Indexable_Post_Indexation_Action class.
79
 
80
  $indexables = [];
81
  foreach ( $post_ids as $post_id ) {
82
+ $indexables[] = $this->builder->build_for_id_and_type( (int) $post_id, 'post' );
 
 
 
83
  }
84
 
85
  return $indexables;
114
  protected function get_query( $count, $limit = 1 ) {
115
  $public_post_types = $this->post_type_helper->get_public_post_types();
116
  $placeholders = \implode( ', ', \array_fill( 0, \count( $public_post_types ), '%s' ) );
117
+ $indexable_table = Model::get_table_name( 'Indexable' );
118
  $replacements = $public_post_types;
119
 
120
  $select = 'ID';
src/actions/indexation/indexable-term-indexation-action.php CHANGED
@@ -11,7 +11,7 @@ use wpdb;
11
  use Yoast\WP\SEO\Builders\Indexable_Builder;
12
  use Yoast\WP\SEO\Helpers\Taxonomy_Helper;
13
  use Yoast\WP\SEO\Models\Indexable;
14
- use Yoast\WP\SEO\ORM\Yoast_Model;
15
 
16
  /**
17
  * Indexable_Term_Indexation_Action class.
@@ -80,10 +80,7 @@ class Indexable_Term_Indexation_Action implements Indexation_Action_Interface {
80
 
81
  $indexables = [];
82
  foreach ( $term_ids as $term_id ) {
83
- $indexable = $this->builder->build_for_id_and_type( (int) $term_id, 'term' );
84
- if ( $indexable !== false ) {
85
- $indexables[] = $indexable;
86
- }
87
  }
88
 
89
  return $indexables;
@@ -118,7 +115,7 @@ class Indexable_Term_Indexation_Action implements Indexation_Action_Interface {
118
  protected function get_query( $count, $limit = 1 ) {
119
  $public_taxonomies = $this->taxonomy->get_public_taxonomies();
120
  $placeholders = \implode( ', ', \array_fill( 0, \count( $public_taxonomies ), '%s' ) );
121
- $indexable_table = Yoast_Model::get_table_name( 'Indexable' );
122
  $replacements = $public_taxonomies;
123
 
124
  $select = 'term_id';
11
  use Yoast\WP\SEO\Builders\Indexable_Builder;
12
  use Yoast\WP\SEO\Helpers\Taxonomy_Helper;
13
  use Yoast\WP\SEO\Models\Indexable;
14
+ use Yoast\WP\Lib\Model;
15
 
16
  /**
17
  * Indexable_Term_Indexation_Action class.
80
 
81
  $indexables = [];
82
  foreach ( $term_ids as $term_id ) {
83
+ $indexables[] = $this->builder->build_for_id_and_type( (int) $term_id, 'term' );
 
 
 
84
  }
85
 
86
  return $indexables;
115
  protected function get_query( $count, $limit = 1 ) {
116
  $public_taxonomies = $this->taxonomy->get_public_taxonomies();
117
  $placeholders = \implode( ', ', \array_fill( 0, \count( $public_taxonomies ), '%s' ) );
118
+ $indexable_table = Model::get_table_name( 'Indexable' );
119
  $replacements = $public_taxonomies;
120
 
121
  $select = 'term_id';
src/builders/indexable-builder.php CHANGED
@@ -147,6 +147,10 @@ class Indexable_Builder {
147
  switch ( $object_type ) {
148
  case 'post':
149
  $indexable = $this->post_builder->build( $object_id, $indexable );
 
 
 
 
150
  $this->primary_term_builder->build( $object_id );
151
 
152
  $author = $this->indexable_repository->find_by_id_and_type( $indexable->author_id, 'user', false );
@@ -165,14 +169,18 @@ class Indexable_Builder {
165
  return $indexable;
166
  }
167
 
168
- // Something went wrong building, nothing to do.
169
  if ( $indexable === false ) {
170
- return false;
 
 
 
 
171
  }
172
 
173
  $this->save_indexable( $indexable, $indexable_before );
174
 
175
- if ( in_array( $object_type, [ 'post', 'term' ], true ) ) {
176
  $this->hierarchy_builder->build( $indexable );
177
  }
178
 
147
  switch ( $object_type ) {
148
  case 'post':
149
  $indexable = $this->post_builder->build( $object_id, $indexable );
150
+ if ( $indexable === false ) {
151
+ break;
152
+ }
153
+
154
  $this->primary_term_builder->build( $object_id );
155
 
156
  $author = $this->indexable_repository->find_by_id_and_type( $indexable->author_id, 'user', false );
169
  return $indexable;
170
  }
171
 
172
+ // Something went wrong building, create a false indexable.
173
  if ( $indexable === false ) {
174
+ $indexable = $this->indexable_repository->query()->create( [
175
+ 'object_id' => $object_id,
176
+ 'object_type' => $object_type,
177
+ 'post_status' => 'unindexed',
178
+ ] );
179
  }
180
 
181
  $this->save_indexable( $indexable, $indexable_before );
182
 
183
+ if ( in_array( $object_type, [ 'post', 'term' ], true ) && $indexable->post_status !== 'unindexed' ) {
184
  $this->hierarchy_builder->build( $indexable );
185
  }
186
 
src/builders/indexable-hierarchy-builder.php CHANGED
@@ -127,7 +127,7 @@ class Indexable_Hierarchy_Builder {
127
 
128
  if ( $post->post_parent !== 0 && $this->post->get_post( $post->post_parent ) !== null ) {
129
  $ancestor = $this->indexable_repository->find_by_id_and_type( $post->post_parent, 'post' );
130
- if ( $ancestor === false ) {
131
  return;
132
  }
133
 
@@ -143,7 +143,7 @@ class Indexable_Hierarchy_Builder {
143
  }
144
 
145
  $ancestor = $this->indexable_repository->find_by_id_and_type( $primary_term_id, 'term' );
146
- if ( $ancestor === false ) {
147
  return;
148
  }
149
 
@@ -166,7 +166,7 @@ class Indexable_Hierarchy_Builder {
166
 
167
  foreach ( $parents as $parent ) {
168
  $ancestor = $this->indexable_repository->find_by_id_and_type( $parent->term_id, 'term' );
169
- if ( $ancestor === false ) {
170
  continue;
171
  }
172
  $this->indexable_hierarchy_repository->add_ancestor( $indexable_id, $ancestor->id, $depth );
127
 
128
  if ( $post->post_parent !== 0 && $this->post->get_post( $post->post_parent ) !== null ) {
129
  $ancestor = $this->indexable_repository->find_by_id_and_type( $post->post_parent, 'post' );
130
+ if ( $ancestor->post_status === 'unindexed' ) {
131
  return;
132
  }
133
 
143
  }
144
 
145
  $ancestor = $this->indexable_repository->find_by_id_and_type( $primary_term_id, 'term' );
146
+ if ( $ancestor->post_status === 'unindexed' ) {
147
  return;
148
  }
149
 
166
 
167
  foreach ( $parents as $parent ) {
168
  $ancestor = $this->indexable_repository->find_by_id_and_type( $parent->term_id, 'term' );
169
+ if ( $ancestor->post_status === 'unindexed' ) {
170
  continue;
171
  }
172
  $this->indexable_hierarchy_repository->add_ancestor( $indexable_id, $ancestor->id, $depth );
src/config/ruckusing-framework.php CHANGED
@@ -8,10 +8,8 @@
8
  namespace Yoast\WP\SEO\Config;
9
 
10
  use wpdb;
 
11
  use Yoast\WP\SEO\Config\Dependency_Management;
12
- use Yoast\WP\SEO\Initializers\Database_Setup;
13
- use Yoast\WP\SEO\Loggers\Migration_Logger;
14
- use YoastSEO_Vendor\Ruckusing_FrameworkRunner;
15
  use YoastSEO_Vendor\Ruckusing_Task_Manager;
16
  use YoastSEO_Vendor\Task_Db_Migrate;
17
 
@@ -34,39 +32,18 @@ class Ruckusing_Framework {
34
  */
35
  protected $dependency_management;
36
 
37
- /**
38
- * The migration logger object.
39
- *
40
- * @var \Yoast\WP\SEO\Loggers\Migration_Logger
41
- */
42
- protected $migration_logger;
43
-
44
- /**
45
- * The database setup object.
46
- *
47
- * @var \Yoast\WP\SEO\Initializers\Database_Setup
48
- */
49
- protected $database_setup;
50
-
51
  /**
52
  * Ruckusing_Framework constructor.
53
  *
54
  * @param \wpdb $wpdb The wpdb instance.
55
  * @param \Yoast\WP\SEO\Config\Dependency_Management $dependency_management The dependency management checker.
56
- * @param \Yoast\WP\SEO\Loggers\Migration_Logger $migration_logger The migration logger, extends the
57
- * Ruckusing logger.
58
- * @param \Yoast\WP\SEO\Initializers\Database_Setup $database_setup The database setup object.
59
  */
60
  public function __construct(
61
  wpdb $wpdb,
62
- Dependency_Management $dependency_management,
63
- Migration_Logger $migration_logger,
64
- Database_Setup $database_setup
65
  ) {
66
  $this->wpdb = $wpdb;
67
  $this->dependency_management = $dependency_management;
68
- $this->migration_logger = $migration_logger;
69
- $this->database_setup = $database_setup;
70
  }
71
 
72
  /**
@@ -75,13 +52,13 @@ class Ruckusing_Framework {
75
  * @param string $migrations_table_name The migrations table name.
76
  * @param string $migrations_directory The migrations directory.
77
  *
78
- * @return \YoastSEO_Vendor\Ruckusing_FrameworkRunner The framework runner.
79
  */
80
  public function get_framework_runner( $migrations_table_name, $migrations_directory ) {
81
  $this->maybe_set_constant();
82
 
83
  $configuration = $this->get_configuration( $migrations_table_name, $migrations_directory );
84
- $instance = new Ruckusing_FrameworkRunner( $configuration, [ 'db:migrate', 'env=production' ], $this->migration_logger );
85
 
86
  /*
87
  * As the Ruckusing_FrameworkRunner is setting its own error and exception handlers,
@@ -119,27 +96,13 @@ class Ruckusing_Framework {
119
  * @return array The configuration
120
  */
121
  public function get_configuration( $migrations_table_name, $migrations_directory ) {
122
- $config = $this->database_setup->get_database_config();
123
-
124
  $ruckusing_config = [
125
  'db' => [
126
  'production' => [
127
- 'type' => 'mysql',
128
- 'host' => $config['host'],
129
- 'port' => $config['port'],
130
- 'database' => \DB_NAME,
131
- 'user' => \DB_USER,
132
- 'password' => \DB_PASSWORD,
133
- 'directory' => '', // This needs to be set, to use the migrations folder as base folder.
134
  'schema_version_table_name' => $migrations_table_name,
135
  ],
136
  ],
137
  'migrations_dir' => [ 'default' => $migrations_directory ],
138
- // This needs to be set but is not used.
139
- 'db_dir' => true,
140
- // This needs to be set but is not used.
141
- 'log_dir' => true,
142
- // This needs to be set but is not used.
143
  ];
144
 
145
  if ( ! empty( $this->wpdb->charset ) ) {
8
  namespace Yoast\WP\SEO\Config;
9
 
10
  use wpdb;
11
+ use Yoast\WP\Lib\Ruckusing_Framework_Runner;
12
  use Yoast\WP\SEO\Config\Dependency_Management;
 
 
 
13
  use YoastSEO_Vendor\Ruckusing_Task_Manager;
14
  use YoastSEO_Vendor\Task_Db_Migrate;
15
 
32
  */
33
  protected $dependency_management;
34
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  /**
36
  * Ruckusing_Framework constructor.
37
  *
38
  * @param \wpdb $wpdb The wpdb instance.
39
  * @param \Yoast\WP\SEO\Config\Dependency_Management $dependency_management The dependency management checker.
 
 
 
40
  */
41
  public function __construct(
42
  wpdb $wpdb,
43
+ Dependency_Management $dependency_management
 
 
44
  ) {
45
  $this->wpdb = $wpdb;
46
  $this->dependency_management = $dependency_management;
 
 
47
  }
48
 
49
  /**
52
  * @param string $migrations_table_name The migrations table name.
53
  * @param string $migrations_directory The migrations directory.
54
  *
55
+ * @return Ruckusing_Framework_Runner The framework runner.
56
  */
57
  public function get_framework_runner( $migrations_table_name, $migrations_directory ) {
58
  $this->maybe_set_constant();
59
 
60
  $configuration = $this->get_configuration( $migrations_table_name, $migrations_directory );
61
+ $instance = new Ruckusing_Framework_Runner( $configuration, [ 'db:migrate', 'env=production' ] );
62
 
63
  /*
64
  * As the Ruckusing_FrameworkRunner is setting its own error and exception handlers,
96
  * @return array The configuration
97
  */
98
  public function get_configuration( $migrations_table_name, $migrations_directory ) {
 
 
99
  $ruckusing_config = [
100
  'db' => [
101
  'production' => [
 
 
 
 
 
 
 
102
  'schema_version_table_name' => $migrations_table_name,
103
  ],
104
  ],
105
  'migrations_dir' => [ 'default' => $migrations_directory ],
 
 
 
 
 
106
  ];
107
 
108
  if ( ! empty( $this->wpdb->charset ) ) {
src/generated/container.php CHANGED
@@ -106,7 +106,6 @@ class Cached_Container extends Container
106
  'yoast\\wp\\seo\\helpers\\twitter\\image_helper' => 'Yoast\\WP\\SEO\\Helpers\\Twitter\\Image_Helper',
107
  'yoast\\wp\\seo\\helpers\\url_helper' => 'Yoast\\WP\\SEO\\Helpers\\Url_Helper',
108
  'yoast\\wp\\seo\\helpers\\user_helper' => 'Yoast\\WP\\SEO\\Helpers\\User_Helper',
109
- 'yoast\\wp\\seo\\initializers\\database_setup' => 'Yoast\\WP\\SEO\\Initializers\\Database_Setup',
110
  'yoast\\wp\\seo\\initializers\\migration_runner' => 'Yoast\\WP\\SEO\\Initializers\\Migration_Runner',
111
  'yoast\\wp\\seo\\integrations\\admin\\indexation_integration' => 'Yoast\\WP\\SEO\\Integrations\\Admin\\Indexation_Integration',
112
  'yoast\\wp\\seo\\integrations\\admin\\migration_error_integration' => 'Yoast\\WP\\SEO\\Integrations\\Admin\\Migration_Error_Integration',
@@ -141,7 +140,6 @@ class Cached_Container extends Container
141
  'yoast\\wp\\seo\\integrations\\watchers\\option_titles_watcher' => 'Yoast\\WP\\SEO\\Integrations\\Watchers\\Option_Titles_Watcher',
142
  'yoast\\wp\\seo\\integrations\\watchers\\primary_term_watcher' => 'Yoast\\WP\\SEO\\Integrations\\Watchers\\Primary_Term_Watcher',
143
  'yoast\\wp\\seo\\loader' => 'Yoast\\WP\\SEO\\Loader',
144
- 'yoast\\wp\\seo\\loggers\\database_logger' => 'Yoast\\WP\\SEO\\Loggers\\Database_Logger',
145
  'yoast\\wp\\seo\\loggers\\logger' => 'Yoast\\WP\\SEO\\Loggers\\Logger',
146
  'yoast\\wp\\seo\\loggers\\migration_logger' => 'Yoast\\WP\\SEO\\Loggers\\Migration_Logger',
147
  'yoast\\wp\\seo\\memoizers\\meta_tags_context_memoizer' => 'Yoast\\WP\\SEO\\Memoizers\\Meta_Tags_Context_Memoizer',
@@ -261,7 +259,6 @@ class Cached_Container extends Container
261
  'Yoast\\WP\\SEO\\Helpers\\Twitter\\Image_Helper' => 'getImageHelper4Service',
262
  'Yoast\\WP\\SEO\\Helpers\\Url_Helper' => 'getUrlHelperService',
263
  'Yoast\\WP\\SEO\\Helpers\\User_Helper' => 'getUserHelperService',
264
- 'Yoast\\WP\\SEO\\Initializers\\Database_Setup' => 'getDatabaseSetupService',
265
  'Yoast\\WP\\SEO\\Initializers\\Migration_Runner' => 'getMigrationRunnerService',
266
  'Yoast\\WP\\SEO\\Integrations\\Admin\\Indexation_Integration' => 'getIndexationIntegrationService',
267
  'Yoast\\WP\\SEO\\Integrations\\Admin\\Migration_Error_Integration' => 'getMigrationErrorIntegrationService',
@@ -296,7 +293,6 @@ class Cached_Container extends Container
296
  'Yoast\\WP\\SEO\\Integrations\\Watchers\\Option_Titles_Watcher' => 'getOptionTitlesWatcherService',
297
  'Yoast\\WP\\SEO\\Integrations\\Watchers\\Primary_Term_Watcher' => 'getPrimaryTermWatcherService',
298
  'Yoast\\WP\\SEO\\Loader' => 'getLoaderService',
299
- 'Yoast\\WP\\SEO\\Loggers\\Database_Logger' => 'getDatabaseLoggerService',
300
  'Yoast\\WP\\SEO\\Loggers\\Logger' => 'getLoggerService',
301
  'Yoast\\WP\\SEO\\Loggers\\Migration_Logger' => 'getMigrationLoggerService',
302
  'Yoast\\WP\\SEO\\Memoizers\\Meta_Tags_Context_Memoizer' => 'getMetaTagsContextMemoizerService',
@@ -350,6 +346,7 @@ class Cached_Container extends Container
350
  'YoastSEO_Vendor\\Symfony\\Component\\DependencyInjection\\ContainerInterface' => true,
351
  'YoastSEO_Vendor\\YoastSEO_Vendor\\Symfony\\Component\\DependencyInjection\\ContainerInterface' => true,
352
  'Yoast\\WP\\SEO\\Commands\\Command_Interface' => true,
 
353
  'wpdb' => true,
354
  ];
355
  }
@@ -388,7 +385,7 @@ class Cached_Container extends Container
388
  */
389
  protected function getWPSEOBreadcrumbsService()
390
  {
391
- return $this->services['WPSEO_Breadcrumbs'] = new \WPSEO_Breadcrumbs(${($_ = isset($this->services['Yoast\\WP\\SEO\\Memoizers\\Meta_Tags_Context_Memoizer']) ? $this->services['Yoast\\WP\\SEO\\Memoizers\\Meta_Tags_Context_Memoizer'] : $this->getMetaTagsContextMemoizerService()) && false ?: '_'}, ${($_ = isset($this->services['Yoast\\WP\\SEO\\Surfaces\\Helpers_Surface']) ? $this->services['Yoast\\WP\\SEO\\Surfaces\\Helpers_Surface'] : $this->getHelpersSurfaceService()) && false ?: '_'}, ${($_ = isset($this->services['WPSEO_Replace_Vars']) ? $this->services['WPSEO_Replace_Vars'] : $this->getWPSEOReplaceVarsService()) && false ?: '_'});
392
  }
393
 
394
  /**
@@ -398,7 +395,7 @@ class Cached_Container extends Container
398
  */
399
  protected function getWPSEOFrontendService()
400
  {
401
- return $this->services['WPSEO_Frontend'] = new \WPSEO_Frontend(${($_ = isset($this->services['Yoast\\WP\\SEO\\Memoizers\\Meta_Tags_Context_Memoizer']) ? $this->services['Yoast\\WP\\SEO\\Memoizers\\Meta_Tags_Context_Memoizer'] : $this->getMetaTagsContextMemoizerService()) && false ?: '_'}, ${($_ = isset($this->services['WPSEO_Replace_Vars']) ? $this->services['WPSEO_Replace_Vars'] : $this->getWPSEOReplaceVarsService()) && false ?: '_'});
402
  }
403
 
404
  /**
@@ -770,7 +767,7 @@ class Cached_Container extends Container
770
  */
771
  protected function getRuckusingFrameworkService()
772
  {
773
- return $this->services['Yoast\\WP\\SEO\\Config\\Ruckusing_Framework'] = new \Yoast\WP\SEO\Config\Ruckusing_Framework(${($_ = isset($this->services['wpdb']) ? $this->services['wpdb'] : $this->getWpdbService()) && false ?: '_'}, ${($_ = isset($this->services['Yoast\\WP\\SEO\\Config\\Dependency_Management']) ? $this->services['Yoast\\WP\\SEO\\Config\\Dependency_Management'] : ($this->services['Yoast\\WP\\SEO\\Config\\Dependency_Management'] = new \Yoast\WP\SEO\Config\Dependency_Management())) && false ?: '_'}, ${($_ = isset($this->services['Yoast\\WP\\SEO\\Loggers\\Migration_Logger']) ? $this->services['Yoast\\WP\\SEO\\Loggers\\Migration_Logger'] : $this->getMigrationLoggerService()) && false ?: '_'}, ${($_ = isset($this->services['Yoast\\WP\\SEO\\Initializers\\Database_Setup']) ? $this->services['Yoast\\WP\\SEO\\Initializers\\Database_Setup'] : $this->getDatabaseSetupService()) && false ?: '_'});
774
  }
775
 
776
  /**
@@ -1223,16 +1220,6 @@ class Cached_Container extends Container
1223
  return $this->services['Yoast\\WP\\SEO\\Helpers\\User_Helper'] = new \Yoast\WP\SEO\Helpers\User_Helper();
1224
  }
1225
 
1226
- /**
1227
- * Gets the public 'Yoast\WP\SEO\Initializers\Database_Setup' shared autowired service.
1228
- *
1229
- * @return \Yoast\WP\SEO\Initializers\Database_Setup
1230
- */
1231
- protected function getDatabaseSetupService()
1232
- {
1233
- return $this->services['Yoast\\WP\\SEO\\Initializers\\Database_Setup'] = new \Yoast\WP\SEO\Initializers\Database_Setup(${($_ = isset($this->services['Yoast\\WP\\SEO\\Loggers\\Logger']) ? $this->services['Yoast\\WP\\SEO\\Loggers\\Logger'] : ($this->services['Yoast\\WP\\SEO\\Loggers\\Logger'] = new \Yoast\WP\SEO\Loggers\Logger())) && false ?: '_'}, ${($_ = isset($this->services['wpdb']) ? $this->services['wpdb'] : $this->getWpdbService()) && false ?: '_'});
1234
- }
1235
-
1236
  /**
1237
  * Gets the public 'Yoast\WP\SEO\Initializers\Migration_Runner' shared autowired service.
1238
  *
@@ -1240,7 +1227,7 @@ class Cached_Container extends Container
1240
  */
1241
  protected function getMigrationRunnerService()
1242
  {
1243
- return $this->services['Yoast\\WP\\SEO\\Initializers\\Migration_Runner'] = new \Yoast\WP\SEO\Initializers\Migration_Runner(${($_ = isset($this->services['Yoast\\WP\\SEO\\Config\\Migration_Status']) ? $this->services['Yoast\\WP\\SEO\\Config\\Migration_Status'] : ($this->services['Yoast\\WP\\SEO\\Config\\Migration_Status'] = new \Yoast\WP\SEO\Config\Migration_Status())) && false ?: '_'}, ${($_ = isset($this->services['Yoast\\WP\\SEO\\Config\\Ruckusing_Framework']) ? $this->services['Yoast\\WP\\SEO\\Config\\Ruckusing_Framework'] : $this->getRuckusingFrameworkService()) && false ?: '_'}, ${($_ = isset($this->services['Yoast\\WP\\SEO\\Loggers\\Logger']) ? $this->services['Yoast\\WP\\SEO\\Loggers\\Logger'] : ($this->services['Yoast\\WP\\SEO\\Loggers\\Logger'] = new \Yoast\WP\SEO\Loggers\Logger())) && false ?: '_'}, ${($_ = isset($this->services['Yoast\\WP\\SEO\\Initializers\\Database_Setup']) ? $this->services['Yoast\\WP\\SEO\\Initializers\\Database_Setup'] : $this->getDatabaseSetupService()) && false ?: '_'});
1244
  }
1245
 
1246
  /**
@@ -1440,7 +1427,7 @@ class Cached_Container extends Container
1440
  */
1441
  protected function getWooCommerceService()
1442
  {
1443
- return $this->services['Yoast\\WP\\SEO\\Integrations\\Third_Party\\WooCommerce'] = new \Yoast\WP\SEO\Integrations\Third_Party\WooCommerce(${($_ = isset($this->services['Yoast\\WP\\SEO\\Helpers\\Options_Helper']) ? $this->services['Yoast\\WP\\SEO\\Helpers\\Options_Helper'] : ($this->services['Yoast\\WP\\SEO\\Helpers\\Options_Helper'] = new \Yoast\WP\SEO\Helpers\Options_Helper())) && false ?: '_'}, ${($_ = isset($this->services['WPSEO_Replace_Vars']) ? $this->services['WPSEO_Replace_Vars'] : $this->getWPSEOReplaceVarsService()) && false ?: '_'});
1444
  }
1445
 
1446
  /**
@@ -1572,10 +1559,7 @@ class Cached_Container extends Container
1572
  {
1573
  $this->services['Yoast\\WP\\SEO\\Loader'] = $instance = new \Yoast\WP\SEO\Loader($this);
1574
 
1575
- $instance->register_initializer('WPSEO_Breadcrumbs');
1576
- $instance->register_initializer('WPSEO_Frontend');
1577
  $instance->register_command('Yoast\\WP\\SEO\\Commands\\Index_Command');
1578
- $instance->register_initializer('Yoast\\WP\\SEO\\Initializers\\Database_Setup');
1579
  $instance->register_initializer('Yoast\\WP\\SEO\\Initializers\\Migration_Runner');
1580
  $instance->register_integration('Yoast\\WP\\SEO\\Integrations\\Admin\\Indexation_Integration');
1581
  $instance->register_integration('Yoast\\WP\\SEO\\Integrations\\Admin\\Migration_Error_Integration');
@@ -1609,7 +1593,6 @@ class Cached_Container extends Container
1609
  $instance->register_integration('Yoast\\WP\\SEO\\Integrations\\Watchers\\Indexable_Term_Watcher');
1610
  $instance->register_integration('Yoast\\WP\\SEO\\Integrations\\Watchers\\Option_Titles_Watcher');
1611
  $instance->register_integration('Yoast\\WP\\SEO\\Integrations\\Watchers\\Primary_Term_Watcher');
1612
- $instance->register_integration('Yoast\\WP\\SEO\\Loggers\\Database_Logger');
1613
  $instance->register_route('Yoast\\WP\\SEO\\Routes\\Indexable_Indexation_Route');
1614
  $instance->register_route('Yoast\\WP\\SEO\\Routes\\Indexables_Head_Route');
1615
  $instance->register_route('Yoast\\WP\\SEO\\Routes\\Yoast_Head_REST_Field');
@@ -1617,16 +1600,6 @@ class Cached_Container extends Container
1617
  return $instance;
1618
  }
1619
 
1620
- /**
1621
- * Gets the public 'Yoast\WP\SEO\Loggers\Database_Logger' shared autowired service.
1622
- *
1623
- * @return \Yoast\WP\SEO\Loggers\Database_Logger
1624
- */
1625
- protected function getDatabaseLoggerService()
1626
- {
1627
- return $this->services['Yoast\\WP\\SEO\\Loggers\\Database_Logger'] = new \Yoast\WP\SEO\Loggers\Database_Logger();
1628
- }
1629
-
1630
  /**
1631
  * Gets the public 'Yoast\WP\SEO\Loggers\Logger' shared autowired service.
1632
  *
106
  'yoast\\wp\\seo\\helpers\\twitter\\image_helper' => 'Yoast\\WP\\SEO\\Helpers\\Twitter\\Image_Helper',
107
  'yoast\\wp\\seo\\helpers\\url_helper' => 'Yoast\\WP\\SEO\\Helpers\\Url_Helper',
108
  'yoast\\wp\\seo\\helpers\\user_helper' => 'Yoast\\WP\\SEO\\Helpers\\User_Helper',
 
109
  'yoast\\wp\\seo\\initializers\\migration_runner' => 'Yoast\\WP\\SEO\\Initializers\\Migration_Runner',
110
  'yoast\\wp\\seo\\integrations\\admin\\indexation_integration' => 'Yoast\\WP\\SEO\\Integrations\\Admin\\Indexation_Integration',
111
  'yoast\\wp\\seo\\integrations\\admin\\migration_error_integration' => 'Yoast\\WP\\SEO\\Integrations\\Admin\\Migration_Error_Integration',
140
  'yoast\\wp\\seo\\integrations\\watchers\\option_titles_watcher' => 'Yoast\\WP\\SEO\\Integrations\\Watchers\\Option_Titles_Watcher',
141
  'yoast\\wp\\seo\\integrations\\watchers\\primary_term_watcher' => 'Yoast\\WP\\SEO\\Integrations\\Watchers\\Primary_Term_Watcher',
142
  'yoast\\wp\\seo\\loader' => 'Yoast\\WP\\SEO\\Loader',
 
143
  'yoast\\wp\\seo\\loggers\\logger' => 'Yoast\\WP\\SEO\\Loggers\\Logger',
144
  'yoast\\wp\\seo\\loggers\\migration_logger' => 'Yoast\\WP\\SEO\\Loggers\\Migration_Logger',
145
  'yoast\\wp\\seo\\memoizers\\meta_tags_context_memoizer' => 'Yoast\\WP\\SEO\\Memoizers\\Meta_Tags_Context_Memoizer',
259
  'Yoast\\WP\\SEO\\Helpers\\Twitter\\Image_Helper' => 'getImageHelper4Service',
260
  'Yoast\\WP\\SEO\\Helpers\\Url_Helper' => 'getUrlHelperService',
261
  'Yoast\\WP\\SEO\\Helpers\\User_Helper' => 'getUserHelperService',
 
262
  'Yoast\\WP\\SEO\\Initializers\\Migration_Runner' => 'getMigrationRunnerService',
263
  'Yoast\\WP\\SEO\\Integrations\\Admin\\Indexation_Integration' => 'getIndexationIntegrationService',
264
  'Yoast\\WP\\SEO\\Integrations\\Admin\\Migration_Error_Integration' => 'getMigrationErrorIntegrationService',
293
  'Yoast\\WP\\SEO\\Integrations\\Watchers\\Option_Titles_Watcher' => 'getOptionTitlesWatcherService',
294
  'Yoast\\WP\\SEO\\Integrations\\Watchers\\Primary_Term_Watcher' => 'getPrimaryTermWatcherService',
295
  'Yoast\\WP\\SEO\\Loader' => 'getLoaderService',
 
296
  'Yoast\\WP\\SEO\\Loggers\\Logger' => 'getLoggerService',
297
  'Yoast\\WP\\SEO\\Loggers\\Migration_Logger' => 'getMigrationLoggerService',
298
  'Yoast\\WP\\SEO\\Memoizers\\Meta_Tags_Context_Memoizer' => 'getMetaTagsContextMemoizerService',
346
  'YoastSEO_Vendor\\Symfony\\Component\\DependencyInjection\\ContainerInterface' => true,
347
  'YoastSEO_Vendor\\YoastSEO_Vendor\\Symfony\\Component\\DependencyInjection\\ContainerInterface' => true,
348
  'Yoast\\WP\\SEO\\Commands\\Command_Interface' => true,
349
+ 'Yoast\\WP\\SEO\\Initializers\\Initializer_Interface' => true,
350
  'wpdb' => true,
351
  ];
352
  }
385
  */
386
  protected function getWPSEOBreadcrumbsService()
387
  {
388
+ return $this->services['WPSEO_Breadcrumbs'] = new \WPSEO_Breadcrumbs();
389
  }
390
 
391
  /**
395
  */
396
  protected function getWPSEOFrontendService()
397
  {
398
+ return $this->services['WPSEO_Frontend'] = new \WPSEO_Frontend();
399
  }
400
 
401
  /**
767
  */
768
  protected function getRuckusingFrameworkService()
769
  {
770
+ return $this->services['Yoast\\WP\\SEO\\Config\\Ruckusing_Framework'] = new \Yoast\WP\SEO\Config\Ruckusing_Framework(${($_ = isset($this->services['wpdb']) ? $this->services['wpdb'] : $this->getWpdbService()) && false ?: '_'}, ${($_ = isset($this->services['Yoast\\WP\\SEO\\Config\\Dependency_Management']) ? $this->services['Yoast\\WP\\SEO\\Config\\Dependency_Management'] : ($this->services['Yoast\\WP\\SEO\\Config\\Dependency_Management'] = new \Yoast\WP\SEO\Config\Dependency_Management())) && false ?: '_'});
771
  }
772
 
773
  /**
1220
  return $this->services['Yoast\\WP\\SEO\\Helpers\\User_Helper'] = new \Yoast\WP\SEO\Helpers\User_Helper();
1221
  }
1222
 
 
 
 
 
 
 
 
 
 
 
1223
  /**
1224
  * Gets the public 'Yoast\WP\SEO\Initializers\Migration_Runner' shared autowired service.
1225
  *
1227
  */
1228
  protected function getMigrationRunnerService()
1229
  {
1230
+ return $this->services['Yoast\\WP\\SEO\\Initializers\\Migration_Runner'] = new \Yoast\WP\SEO\Initializers\Migration_Runner(${($_ = isset($this->services['Yoast\\WP\\SEO\\Config\\Migration_Status']) ? $this->services['Yoast\\WP\\SEO\\Config\\Migration_Status'] : ($this->services['Yoast\\WP\\SEO\\Config\\Migration_Status'] = new \Yoast\WP\SEO\Config\Migration_Status())) && false ?: '_'}, ${($_ = isset($this->services['Yoast\\WP\\SEO\\Config\\Ruckusing_Framework']) ? $this->services['Yoast\\WP\\SEO\\Config\\Ruckusing_Framework'] : $this->getRuckusingFrameworkService()) && false ?: '_'});
1231
  }
1232
 
1233
  /**
1427
  */
1428
  protected function getWooCommerceService()
1429
  {
1430
+ return $this->services['Yoast\\WP\\SEO\\Integrations\\Third_Party\\WooCommerce'] = new \Yoast\WP\SEO\Integrations\Third_Party\WooCommerce(${($_ = isset($this->services['Yoast\\WP\\SEO\\Helpers\\Options_Helper']) ? $this->services['Yoast\\WP\\SEO\\Helpers\\Options_Helper'] : ($this->services['Yoast\\WP\\SEO\\Helpers\\Options_Helper'] = new \Yoast\WP\SEO\Helpers\Options_Helper())) && false ?: '_'}, ${($_ = isset($this->services['WPSEO_Replace_Vars']) ? $this->services['WPSEO_Replace_Vars'] : $this->getWPSEOReplaceVarsService()) && false ?: '_'}, ${($_ = isset($this->services['Yoast\\WP\\SEO\\Memoizers\\Meta_Tags_Context_Memoizer']) ? $this->services['Yoast\\WP\\SEO\\Memoizers\\Meta_Tags_Context_Memoizer'] : $this->getMetaTagsContextMemoizerService()) && false ?: '_'});
1431
  }
1432
 
1433
  /**
1559
  {
1560
  $this->services['Yoast\\WP\\SEO\\Loader'] = $instance = new \Yoast\WP\SEO\Loader($this);
1561
 
 
 
1562
  $instance->register_command('Yoast\\WP\\SEO\\Commands\\Index_Command');
 
1563
  $instance->register_initializer('Yoast\\WP\\SEO\\Initializers\\Migration_Runner');
1564
  $instance->register_integration('Yoast\\WP\\SEO\\Integrations\\Admin\\Indexation_Integration');
1565
  $instance->register_integration('Yoast\\WP\\SEO\\Integrations\\Admin\\Migration_Error_Integration');
1593
  $instance->register_integration('Yoast\\WP\\SEO\\Integrations\\Watchers\\Indexable_Term_Watcher');
1594
  $instance->register_integration('Yoast\\WP\\SEO\\Integrations\\Watchers\\Option_Titles_Watcher');
1595
  $instance->register_integration('Yoast\\WP\\SEO\\Integrations\\Watchers\\Primary_Term_Watcher');
 
1596
  $instance->register_route('Yoast\\WP\\SEO\\Routes\\Indexable_Indexation_Route');
1597
  $instance->register_route('Yoast\\WP\\SEO\\Routes\\Indexables_Head_Route');
1598
  $instance->register_route('Yoast\\WP\\SEO\\Routes\\Yoast_Head_REST_Field');
1600
  return $instance;
1601
  }
1602
 
 
 
 
 
 
 
 
 
 
 
1603
  /**
1604
  * Gets the public 'Yoast\WP\SEO\Loggers\Logger' shared autowired service.
1605
  *
src/generators/breadcrumbs-generator.php CHANGED
@@ -67,14 +67,14 @@ class Breadcrumbs_Generator implements Generator_Interface {
67
  public function generate( Meta_Tags_Context $context ) {
68
  $static_ancestors = [];
69
  $breadcrumbs_home = $this->options->get( 'breadcrumbs-home' );
70
- if ( $breadcrumbs_home !== '' ) {
71
  $front_page_id = $this->current_page->get_front_page_id();
72
  if ( $front_page_id === 0 ) {
73
  $static_ancestors[] = $this->repository->find_for_home_page();
74
  }
75
  else {
76
  $static_ancestor = $this->repository->find_by_id_and_type( $front_page_id, 'post' );
77
- if ( $static_ancestor !== false ) {
78
  $static_ancestors[] = $static_ancestor;
79
  }
80
  }
@@ -82,7 +82,7 @@ class Breadcrumbs_Generator implements Generator_Interface {
82
  $page_for_posts = \get_option( 'page_for_posts' );
83
  if ( $this->should_have_blog_crumb( $page_for_posts ) ) {
84
  $static_ancestor = $this->repository->find_by_id_and_type( $page_for_posts, 'post' );
85
- if ( $static_ancestor !== false ) {
86
  $static_ancestors[] = $static_ancestor;
87
  }
88
  }
67
  public function generate( Meta_Tags_Context $context ) {
68
  $static_ancestors = [];
69
  $breadcrumbs_home = $this->options->get( 'breadcrumbs-home' );
70
+ if ( $breadcrumbs_home !== '' && ! in_array( $this->current_page->get_page_type(), [ 'Home_Page', 'Static_Home_Page' ], true ) ) {
71
  $front_page_id = $this->current_page->get_front_page_id();
72
  if ( $front_page_id === 0 ) {
73
  $static_ancestors[] = $this->repository->find_for_home_page();
74
  }
75
  else {
76
  $static_ancestor = $this->repository->find_by_id_and_type( $front_page_id, 'post' );
77
+ if ( $static_ancestor->post_status !== 'unindexed' ) {
78
  $static_ancestors[] = $static_ancestor;
79
  }
80
  }
82
  $page_for_posts = \get_option( 'page_for_posts' );
83
  if ( $this->should_have_blog_crumb( $page_for_posts ) ) {
84
  $static_ancestor = $this->repository->find_by_id_and_type( $page_for_posts, 'post' );
85
+ if ( $static_ancestor->post_status !== 'unindexed' ) {
86
  $static_ancestors[] = $static_ancestor;
87
  }
88
  }
src/helpers/author-archive-helper.php CHANGED
@@ -7,7 +7,7 @@
7
 
8
  namespace Yoast\WP\SEO\Helpers;
9
 
10
- use Yoast\WP\SEO\ORM\Yoast_Model;
11
 
12
  /**
13
  * Class Author_Archive_Helper
@@ -61,7 +61,7 @@ class Author_Archive_Helper {
61
  * @return bool Whether the author has at least one public post.
62
  */
63
  protected function author_has_a_public_post( $author_id ) {
64
- $indexable_exists = Yoast_Model::of_type( 'Indexable' )
65
  ->where( 'object_type', 'post' )
66
  ->where_in( 'object_sub_type', $this->get_author_archive_post_types() )
67
  ->where( 'author_id', $author_id )
@@ -82,7 +82,7 @@ class Author_Archive_Helper {
82
  * @return bool Whether the author has at least one post with the is public null.
83
  */
84
  protected function author_has_a_post_with_is_public_null( $author_id ) {
85
- $indexable_exists = Yoast_Model::of_type( 'Indexable' )
86
  ->where( 'object_type', 'post' )
87
  ->where_in( 'object_sub_type', $this->get_author_archive_post_types() )
88
  ->where( 'author_id', $author_id )
7
 
8
  namespace Yoast\WP\SEO\Helpers;
9
 
10
+ use Yoast\WP\Lib\Model;
11
 
12
  /**
13
  * Class Author_Archive_Helper
61
  * @return bool Whether the author has at least one public post.
62
  */
63
  protected function author_has_a_public_post( $author_id ) {
64
+ $indexable_exists = Model::of_type( 'Indexable' )
65
  ->where( 'object_type', 'post' )
66
  ->where_in( 'object_sub_type', $this->get_author_archive_post_types() )
67
  ->where( 'author_id', $author_id )
82
  * @return bool Whether the author has at least one post with the is public null.
83
  */
84
  protected function author_has_a_post_with_is_public_null( $author_id ) {
85
+ $indexable_exists = Model::of_type( 'Indexable' )
86
  ->where( 'object_type', 'post' )
87
  ->where_in( 'object_sub_type', $this->get_author_archive_post_types() )
88
  ->where( 'author_id', $author_id )
src/helpers/post-helper.php CHANGED
@@ -7,7 +7,7 @@
7
 
8
  namespace Yoast\WP\SEO\Helpers;
9
 
10
- use Yoast\WP\SEO\ORM\Yoast_Model;
11
 
12
  /**
13
  * Class Redirect_Helper
@@ -115,16 +115,16 @@ class Post_Helper {
115
  * @return bool Whether the update was successful.
116
  */
117
  public function update_has_public_posts_on_attachments( $post_parent, $has_public_posts ) {
118
- $orm_wrapper = Yoast_Model::of_type( 'Indexable' );
119
 
120
  // Debatable way to get the table name in an update format.
121
  $query = $orm_wrapper->set( 'has_public_posts', $has_public_posts )->get_update_sql();
122
- $query = str_replace( 'WHERE `id` = ?', '', $query );
123
 
124
  // Execute a raw query here to be able to find & set in one, i.e. more performant.
125
  return $orm_wrapper
126
  ->raw_execute(
127
- $query . 'WHERE `object_type` = \'post\' AND `object_sub_type` = \'attachment\' AND `post_status` = \'inherit\' AND `post_parent` = ?',
128
  [ $has_public_posts, $post_parent ]
129
  );
130
  }
7
 
8
  namespace Yoast\WP\SEO\Helpers;
9
 
10
+ use Yoast\WP\Lib\Model;
11
 
12
  /**
13
  * Class Redirect_Helper
115
  * @return bool Whether the update was successful.
116
  */
117
  public function update_has_public_posts_on_attachments( $post_parent, $has_public_posts ) {
118
+ $orm_wrapper = Model::of_type( 'Indexable' );
119
 
120
  // Debatable way to get the table name in an update format.
121
  $query = $orm_wrapper->set( 'has_public_posts', $has_public_posts )->get_update_sql();
122
+ $query = str_replace( 'WHERE `id` = %s', '', $query );
123
 
124
  // Execute a raw query here to be able to find & set in one, i.e. more performant.
125
  return $orm_wrapper
126
  ->raw_execute(
127
+ $query . 'WHERE `object_type` = \'post\' AND `object_sub_type` = \'attachment\' AND `post_status` = \'inherit\' AND `post_parent` = %s',
128
  [ $has_public_posts, $post_parent ]
129
  );
130
  }
src/helpers/robots-helper.php CHANGED
@@ -7,7 +7,6 @@
7
 
8
  namespace Yoast\WP\SEO\Helpers;
9
 
10
- use Yoast\WP\SEO\Models\Indexable;
11
  use Yoast\WP\SEO\Presentations\Indexable_Presentation;
12
 
13
  /**
@@ -24,14 +23,22 @@ class Robots_Helper {
24
  * @return string The altered robots string.
25
  */
26
  public function set_robots_no_index( $robots, Indexable_Presentation $presentation ) {
27
- if ( in_array( 'noindex', $presentation->robots, true ) ) {
 
 
 
 
 
 
28
  return $robots;
29
  }
30
 
31
- $new_robots = $presentation->robots;
32
- $new_robots['index'] = 'noindex';
33
- $new_robots = \array_filter( $new_robots );
 
34
 
35
- return implode( ',', $new_robots );
 
36
  }
37
  }
7
 
8
  namespace Yoast\WP\SEO\Helpers;
9
 
 
10
  use Yoast\WP\SEO\Presentations\Indexable_Presentation;
11
 
12
  /**
23
  * @return string The altered robots string.
24
  */
25
  public function set_robots_no_index( $robots, Indexable_Presentation $presentation ) {
26
+ // When robots is null just return the default but with noindex: `noindex, follow`.
27
+ if ( ! \is_string( $robots ) ) {
28
+ return 'noindex, follow';
29
+ }
30
+
31
+ // Already noindex.
32
+ if ( \strpos( $robots, 'noindex' ) !== false ) {
33
  return $robots;
34
  }
35
 
36
+ // Replace index with noindex.
37
+ if ( \strpos( $robots, 'index' ) !== false ) {
38
+ return \str_replace( 'index', 'noindex', $robots );
39
+ }
40
 
41
+ // Add noindex.
42
+ return 'noindex, ' . $robots;
43
  }
44
  }
src/initializers/database-setup.php DELETED
@@ -1,115 +0,0 @@
1
- <?php
2
- /**
3
- * Yoast SEO Plugin File.
4
- *
5
- * @package Yoast\YoastSEO\Config
6
- */
7
-
8
- namespace Yoast\WP\SEO\Initializers;
9
-
10
- use wpdb;
11
- use Yoast\WP\SEO\Conditionals\No_Conditionals;
12
- use Yoast\WP\SEO\Loggers\Logger;
13
- use Yoast\WP\SEO\ORM\Yoast_Model;
14
- use YoastSEO_Vendor\ORM;
15
- use YoastSEO_Vendor\Psr\Log\LoggerInterface;
16
-
17
- /**
18
- * Configures the ORM with the database credentials.
19
- */
20
- class Database_Setup implements Initializer_Interface {
21
- use No_Conditionals;
22
-
23
- /**
24
- * The database object.
25
- *
26
- * @var wpdb
27
- */
28
- protected $wpdb;
29
-
30
- /**
31
- * The logger object.
32
- *
33
- * @var LoggerInterface
34
- */
35
- protected $logger;
36
-
37
- /**
38
- * Database_Setup constructor.
39
- *
40
- * @param Logger $logger The logger.
41
- * @param wpdb $wpdb The wpdb instance.
42
- */
43
- public function __construct( Logger $logger, wpdb $wpdb ) {
44
- $this->logger = $logger;
45
- $this->wpdb = $wpdb;
46
- }
47
-
48
- /**
49
- * Initializes the database setup.
50
- */
51
- public function initialize() {
52
- ORM::configure( $this->get_connection_string() );
53
- ORM::configure( 'username', \DB_USER );
54
- ORM::configure( 'password', \DB_PASSWORD );
55
-
56
- Yoast_Model::$auto_prefix_models = '\\Yoast\\WP\\SEO\\Models\\';
57
- Yoast_Model::$logger = $this->logger;
58
- }
59
-
60
- /**
61
- * Retrieves the database config from wpdb.
62
- *
63
- * @return array An array with host, port and socket properties.
64
- */
65
- public function get_database_config() {
66
- $host = \DB_HOST;
67
- $port = null;
68
- $socket = null;
69
- $is_ipv6 = false;
70
-
71
- $host_data = $this->wpdb->parse_db_host( \DB_HOST );
72
- if ( $host_data ) {
73
- list( $host, $port, $socket, $is_ipv6 ) = $host_data;
74
- }
75
- if ( $is_ipv6 && extension_loaded( 'mysqlnd' ) ) {
76
- $host = "[$host]";
77
- }
78
- if ( empty( $port ) ) {
79
- $port = ini_get( 'mysqli.default_port' );
80
- }
81
- if ( empty( $port ) ) {
82
- $port = 3306;
83
- }
84
- if ( empty( $socket ) ) {
85
- $socket = ini_get( 'mysqli.default_socket' );
86
- }
87
-
88
- return [
89
- 'host' => $host,
90
- 'port' => $port,
91
- 'socket' => $socket,
92
- ];
93
- }
94
-
95
- /**
96
- * Builds a connection string from wpdb
97
- *
98
- * @return string The connection string.
99
- */
100
- private function get_connection_string() {
101
- $config = $this->get_database_config();
102
-
103
- $connection_string = 'mysql:host=' . $config['host'] . ';dbname=' . \DB_NAME . ';';
104
- if ( ! empty( $config['port'] ) ) {
105
- $connection_string .= 'port=' . $config['port'] . ';';
106
- }
107
- if ( ! empty( $config['socket'] ) ) {
108
- $connection_string .= 'unix_socket=' . $config['socket'] . ';';
109
- }
110
- if ( \defined( 'DB_CHARSET' ) && ! empty( \DB_CHARSET ) ) {
111
- $connection_string .= 'charset=' . \DB_CHARSET . ';';
112
- }
113
- return $connection_string;
114
- }
115
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/initializers/migration-runner.php CHANGED
@@ -8,10 +8,9 @@
8
  namespace Yoast\WP\SEO\Initializers;
9
 
10
  use Exception;
11
- use Yoast\WP\SEO\Config\Ruckusing_Framework;
12
  use Yoast\WP\SEO\Config\Migration_Status;
13
- use Yoast\WP\SEO\Loggers\Logger;
14
- use Yoast\WP\SEO\ORM\Yoast_Model;
15
 
16
  /**
17
  * Triggers database migrations and handles results.
@@ -34,13 +33,6 @@ class Migration_Runner implements Initializer_Interface {
34
  */
35
  protected $framework;
36
 
37
- /**
38
- * The logger object.
39
- *
40
- * @var Logger
41
- */
42
- protected $logger;
43
-
44
  /**
45
  * The migration status.
46
  *
@@ -48,39 +40,24 @@ class Migration_Runner implements Initializer_Interface {
48
  */
49
  protected $migration_status;
50
 
51
- /**
52
- * The database setup object.
53
- *
54
- * @var Database_Setup
55
- */
56
- protected $database_setup;
57
-
58
  /**
59
  * Migrations constructor.
60
  *
61
  * @param Migration_Status $migration_status The migration status.
62
  * @param Ruckusing_Framework $framework The Ruckusing framework runner.
63
- * @param Logger $logger A PSR compatible logger.
64
- * @param Database_Setup $database_setup The database setup.
65
  */
66
  public function __construct(
67
  Migration_Status $migration_status,
68
- Ruckusing_Framework $framework,
69
- Logger $logger,
70
- Database_Setup $database_setup
71
  ) {
72
  $this->migration_status = $migration_status;
73
  $this->framework = $framework;
74
- $this->logger = $logger;
75
- $this->database_setup = $database_setup;
76
  }
77
 
78
  /**
79
  * Runs this initializer.
80
  *
81
  * @throws \Exception When a migration errored.
82
- *
83
- * @return void
84
  */
85
  public function initialize() {
86
  $this->run_free_migrations();
@@ -92,11 +69,9 @@ class Migration_Runner implements Initializer_Interface {
92
  * Runs the free migrations.
93
  *
94
  * @throws \Exception When a migration errored.
95
- *
96
- * @return void
97
  */
98
  public function run_free_migrations() {
99
- $this->run_migrations( 'free', Yoast_Model::get_table_name( 'migrations' ), \WPSEO_PATH . 'migrations' );
100
  }
101
 
102
  /**
@@ -120,12 +95,6 @@ class Migration_Runner implements Initializer_Interface {
120
  }
121
 
122
  try {
123
- $database_config = $this->database_setup->get_database_config();
124
- if ( ! empty( $database_config['socket'] ) ) {
125
- // Temporarily set the defined socket as the default as we can't pass it to ruckusing.
126
- $old_mysqli_socket = @ini_set( 'mysqli.default_socket', $database_config['socket'] );
127
- }
128
-
129
  $framework_runner = $this->framework->get_framework_runner( $migrations_table_name, $migrations_directory );
130
  /**
131
  * This variable represents Ruckusing_Adapter_MySQL_Base adapter.
@@ -148,8 +117,6 @@ class Migration_Runner implements Initializer_Interface {
148
  $task_manager = $this->framework->get_framework_task_manager( $adapter, $migrations_table_name, $migrations_directory );
149
  $task_manager->execute( $framework_runner, 'db:migrate', [] );
150
  } catch ( Exception $exception ) {
151
- $this->logger->error( $exception->getMessage() );
152
-
153
  // Something went wrong...
154
  $this->migration_status->set_error( $name, $exception->getMessage() );
155
 
@@ -158,11 +125,6 @@ class Migration_Runner implements Initializer_Interface {
158
  }
159
 
160
  return false;
161
- } finally {
162
- if ( isset( $old_mysqli_socket ) && $old_mysqli_socket !== false ) {
163
- // Always restore the old default if it was overwritten.
164
- @ini_set( 'mysqli.default_socket', $old_mysqli_socket );
165
- }
166
  }
167
 
168
  $this->migration_status->set_success( $name );
8
  namespace Yoast\WP\SEO\Initializers;
9
 
10
  use Exception;
11
+ use Yoast\WP\Lib\Model;
12
  use Yoast\WP\SEO\Config\Migration_Status;
13
+ use Yoast\WP\SEO\Config\Ruckusing_Framework;
 
14
 
15
  /**
16
  * Triggers database migrations and handles results.
33
  */
34
  protected $framework;
35
 
 
 
 
 
 
 
 
36
  /**
37
  * The migration status.
38
  *
40
  */
41
  protected $migration_status;
42
 
 
 
 
 
 
 
 
43
  /**
44
  * Migrations constructor.
45
  *
46
  * @param Migration_Status $migration_status The migration status.
47
  * @param Ruckusing_Framework $framework The Ruckusing framework runner.
 
 
48
  */
49
  public function __construct(
50
  Migration_Status $migration_status,
51
+ Ruckusing_Framework $framework
 
 
52
  ) {
53
  $this->migration_status = $migration_status;
54
  $this->framework = $framework;
 
 
55
  }
56
 
57
  /**
58
  * Runs this initializer.
59
  *
60
  * @throws \Exception When a migration errored.
 
 
61
  */
62
  public function initialize() {
63
  $this->run_free_migrations();
69
  * Runs the free migrations.
70
  *
71
  * @throws \Exception When a migration errored.
 
 
72
  */
73
  public function run_free_migrations() {
74
+ $this->run_migrations( 'free', Model::get_table_name( 'migrations' ), \WPSEO_PATH . 'migrations' );
75
  }
76
 
77
  /**
95
  }
96
 
97
  try {
 
 
 
 
 
 
98
  $framework_runner = $this->framework->get_framework_runner( $migrations_table_name, $migrations_directory );
99
  /**
100
  * This variable represents Ruckusing_Adapter_MySQL_Base adapter.
117
  $task_manager = $this->framework->get_framework_task_manager( $adapter, $migrations_table_name, $migrations_directory );
118
  $task_manager->execute( $framework_runner, 'db:migrate', [] );
119
  } catch ( Exception $exception ) {
 
 
120
  // Something went wrong...
121
  $this->migration_status->set_error( $name, $exception->getMessage() );
122
 
125
  }
126
 
127
  return false;
 
 
 
 
 
128
  }
129
 
130
  $this->migration_status->set_success( $name );
src/integrations/third-party/woocommerce.php CHANGED
@@ -12,6 +12,7 @@ use Yoast\WP\SEO\Conditionals\Front_End_Conditional;
12
  use Yoast\WP\SEO\Conditionals\WooCommerce_Conditional;
13
  use Yoast\WP\SEO\Helpers\Options_Helper;
14
  use Yoast\WP\SEO\Integrations\Integration_Interface;
 
15
  use Yoast\WP\SEO\Presentations\Indexable_Presentation;
16
 
17
  /**
@@ -33,6 +34,13 @@ class WooCommerce implements Integration_Interface {
33
  */
34
  private $replace_vars;
35
 
 
 
 
 
 
 
 
36
  /**
37
  * @inheritDoc
38
  */
@@ -43,12 +51,18 @@ class WooCommerce implements Integration_Interface {
43
  /**
44
  * WooCommerce constructor.
45
  *
46
- * @param Options_Helper $options The options helper.
47
- * @param WPSEO_Replace_Vars $replace_vars The replace vars helper.
 
48
  */
49
- public function __construct( Options_Helper $options, WPSEO_Replace_Vars $replace_vars ) {
50
- $this->options = $options;
51
- $this->replace_vars = $replace_vars;
 
 
 
 
 
52
  }
53
 
54
  /**
@@ -83,7 +97,9 @@ class WooCommerce implements Integration_Interface {
83
  *
84
  * @return string The title to use.
85
  */
86
- public function title( $title, Indexable_Presentation $presentation ) {
 
 
87
  if ( $presentation->model->title ) {
88
  return $title;
89
  }
@@ -117,7 +133,9 @@ class WooCommerce implements Integration_Interface {
117
  *
118
  * @return string The description to use.
119
  */
120
- public function description( $description, Indexable_Presentation $presentation ) {
 
 
121
  if ( $presentation->model->description ) {
122
  return $description;
123
  }
@@ -187,4 +205,20 @@ class WooCommerce implements Integration_Interface {
187
 
188
  return \wc_get_page_id( 'shop' );
189
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
190
  }
12
  use Yoast\WP\SEO\Conditionals\WooCommerce_Conditional;
13
  use Yoast\WP\SEO\Helpers\Options_Helper;
14
  use Yoast\WP\SEO\Integrations\Integration_Interface;
15
+ use Yoast\WP\SEO\Memoizers\Meta_Tags_Context_Memoizer;
16
  use Yoast\WP\SEO\Presentations\Indexable_Presentation;
17
 
18
  /**
34
  */
35
  private $replace_vars;
36
 
37
+ /**
38
+ * The memoizer for the meta tags context.
39
+ *
40
+ * @var Meta_Tags_Context_Memoizer
41
+ */
42
+ protected $context_memoizer;
43
+
44
  /**
45
  * @inheritDoc
46
  */
51
  /**
52
  * WooCommerce constructor.
53
  *
54
+ * @param Options_Helper $options The options helper.
55
+ * @param WPSEO_Replace_Vars $replace_vars The replace vars helper.
56
+ * @param Meta_Tags_Context_Memoizer $context_memoizer The meta tags context memoizer.
57
  */
58
+ public function __construct(
59
+ Options_Helper $options,
60
+ WPSEO_Replace_Vars $replace_vars,
61
+ Meta_Tags_Context_Memoizer $context_memoizer
62
+ ) {
63
+ $this->options = $options;
64
+ $this->replace_vars = $replace_vars;
65
+ $this->context_memoizer = $context_memoizer;
66
  }
67
 
68
  /**
97
  *
98
  * @return string The title to use.
99
  */
100
+ public function title( $title, $presentation = null ) {
101
+ $presentation = $this->ensure_presentation( $presentation );
102
+
103
  if ( $presentation->model->title ) {
104
  return $title;
105
  }
133
  *
134
  * @return string The description to use.
135
  */
136
+ public function description( $description, $presentation = null ) {
137
+ $presentation = $this->ensure_presentation( $presentation );
138
+
139
  if ( $presentation->model->description ) {
140
  return $description;
141
  }
205
 
206
  return \wc_get_page_id( 'shop' );
207
  }
208
+
209
+ /**
210
+ * Ensures a presentation is available.
211
+ *
212
+ * @param Indexable_Presentation $presentation The indexable presentation.
213
+ *
214
+ * @return Indexable_Presentation The presentation, taken from the current page if the input was invalid.
215
+ */
216
+ protected function ensure_presentation( $presentation ) {
217
+ if ( \is_a( $presentation, Indexable_Presentation::class ) ) {
218
+ return $presentation;
219
+ }
220
+
221
+ $context = $this->context_memoizer->for_current_page();
222
+ return $context->presentation;
223
+ }
224
  }
src/integrations/watchers/indexable-permalink-watcher.php CHANGED
@@ -10,7 +10,7 @@ namespace Yoast\WP\SEO\Integrations\Watchers;
10
  use Yoast\WP\SEO\Conditionals\Migrations_Conditional;
11
  use Yoast\WP\SEO\Helpers\Post_Type_Helper;
12
  use Yoast\WP\SEO\Integrations\Integration_Interface;
13
- use Yoast\WP\SEO\ORM\Yoast_Model;
14
  use Yoast\WP\SEO\WordPress\Wrapper;
15
 
16
  /**
@@ -159,7 +159,7 @@ class Indexable_Permalink_Watcher implements Integration_Interface {
159
  }
160
 
161
  Wrapper::get_wpdb()->update(
162
- Yoast_Model::get_table_name( 'Indexable' ),
163
  [
164
  'permalink' => null,
165
  ],
10
  use Yoast\WP\SEO\Conditionals\Migrations_Conditional;
11
  use Yoast\WP\SEO\Helpers\Post_Type_Helper;
12
  use Yoast\WP\SEO\Integrations\Integration_Interface;
13
+ use Yoast\WP\Lib\Model;
14
  use Yoast\WP\SEO\WordPress\Wrapper;
15
 
16
  /**
159
  }
160
 
161
  Wrapper::get_wpdb()->update(
162
+ Model::get_table_name( 'Indexable' ),
163
  [
164
  'permalink' => null,
165
  ],
src/integrations/watchers/indexable-post-watcher.php CHANGED
@@ -198,10 +198,6 @@ class Indexable_Post_Watcher implements Integration_Interface {
198
  // Update the author indexable's has public posts value.
199
  try {
200
  $author_indexable = $this->repository->find_by_id_and_type( $indexable->author_id, 'user' );
201
- if ( $author_indexable === false ) {
202
- return;
203
- }
204
-
205
  $author_indexable->has_public_posts = $this->author_archive->author_has_public_posts( $author_indexable->object_id );
206
  $author_indexable->save();
207
  } catch ( Exception $exception ) { // @codingStandardsIgnoreLine Generic.CodeAnalysis.EmptyStatement.DetectedCATCH -- There is nothing to do.
198
  // Update the author indexable's has public posts value.
199
  try {
200
  $author_indexable = $this->repository->find_by_id_and_type( $indexable->author_id, 'user' );
 
 
 
 
201
  $author_indexable->has_public_posts = $this->author_archive->author_has_public_posts( $author_indexable->object_id );
202
  $author_indexable->save();
203
  } catch ( Exception $exception ) { // @codingStandardsIgnoreLine Generic.CodeAnalysis.EmptyStatement.DetectedCATCH -- There is nothing to do.
src/integrations/watchers/option-titles-watcher.php CHANGED
@@ -9,7 +9,7 @@ namespace Yoast\WP\SEO\Integrations\Watchers;
9
 
10
  use Yoast\WP\SEO\Conditionals\Migrations_Conditional;
11
  use Yoast\WP\SEO\Integrations\Integration_Interface;
12
- use Yoast\WP\SEO\ORM\Yoast_Model;
13
  use Yoast\WP\SEO\WordPress\Wrapper;
14
 
15
  /**
@@ -111,14 +111,14 @@ class Option_Titles_Watcher implements Integration_Interface {
111
  $total = \count( $post_types );
112
  $placeholders = \array_fill( 0, $total, '%s' );
113
  $placeholders = \implode( ', ', $placeholders );
114
- $hierarchy_table = Yoast_Model::get_table_name( 'Indexable_Hierarchy' );
115
- $indexable_table = Yoast_Model::get_table_name( 'Indexable' );
116
 
117
  $result = $wpdb->query(
118
  $wpdb->prepare( "
119
  DELETE FROM `$hierarchy_table`
120
- WHERE indexable_id IN(
121
- SELECT id FROM `$indexable_table` WHERE object_type = 'post' AND object_sub_type IN( $placeholders )
122
  )",
123
  $post_types
124
  )
9
 
10
  use Yoast\WP\SEO\Conditionals\Migrations_Conditional;
11
  use Yoast\WP\SEO\Integrations\Integration_Interface;
12
+ use Yoast\WP\Lib\Model;
13
  use Yoast\WP\SEO\WordPress\Wrapper;
14
 
15
  /**
111
  $total = \count( $post_types );
112
  $placeholders = \array_fill( 0, $total, '%s' );
113
  $placeholders = \implode( ', ', $placeholders );
114
+ $hierarchy_table = Model::get_table_name( 'Indexable_Hierarchy' );
115
+ $indexable_table = Model::get_table_name( 'Indexable' );
116
 
117
  $result = $wpdb->query(
118
  $wpdb->prepare( "
119
  DELETE FROM `$hierarchy_table`
120
+ WHERE indexable_id IN(
121
+ SELECT id FROM `$indexable_table` WHERE object_type = 'post' AND object_sub_type IN( $placeholders )
122
  )",
123
  $post_types
124
  )
src/loggers/database-logger.php DELETED
@@ -1,173 +0,0 @@
1
- <?php
2
- /**
3
- * A simple logger to allow query and memory usage logging.
4
- *
5
- * @package Yoast\WP\SEO\Loggers
6
- */
7
-
8
- namespace Yoast\WP\SEO\Loggers;
9
-
10
- use Yoast\WP\SEO\Conditionals\Development_Conditional;
11
- use Yoast\WP\SEO\Integrations\Integration_Interface;
12
- use YoastSEO_Vendor\ORM;
13
-
14
- /**
15
- * Class Database_Logger
16
- */
17
- class Database_Logger implements Integration_Interface {
18
- /**
19
- * Private array of queries used for logging.
20
- *
21
- * @var array
22
- */
23
- protected $query_log = [];
24
-
25
- /**
26
- * @inheritDoc
27
- */
28
- public function register_hooks() {
29
- ORM::configure( 'logging', true );
30
- ORM::configure( 'logger', [ $this, 'logger' ] );
31
-
32
- \add_action( 'shutdown', [ $this, 'log_output' ] );
33
- }
34
-
35
- /**
36
- * @inheritDoc
37
- */
38
- public static function get_conditionals() {
39
- return [ Development_Conditional::class ];
40
- }
41
-
42
- /**
43
- * Logs the query to a local variable for output on shutdown.
44
- *
45
- * @param string $query The query.
46
- * @param float $time The time the query took to execute.
47
- *
48
- * @return void
49
- */
50
- public function logger( $query, $time ) {
51
- $query = [
52
- 'query' => $query,
53
- 'time' => $time,
54
- ];
55
- $this->query_log[] = $query;
56
- }
57
-
58
- /**
59
- * Outputs some logging.
60
- *
61
- * @return void
62
- */
63
- public function log_output() {
64
- $content_type = $this->get_content_type();
65
-
66
- if (
67
- \wp_doing_ajax() ||
68
- ( defined( 'WP_CLI' ) && WP_CLI ) ||
69
- ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ||
70
- ( stripos( $content_type, 'text/html' ) === false && $content_type !== '' )
71
- ) {
72
- return;
73
- }
74
-
75
- echo PHP_EOL, PHP_EOL, '<!--';
76
-
77
- $this->log_time();
78
- $this->log_memory_usage();
79
- $this->log_idiorm_queries();
80
- $this->log_wpdb_queries();
81
-
82
- echo '-->', PHP_EOL;
83
- }
84
-
85
- /**
86
- * Get the content type from the return header.
87
- *
88
- * @return string The return header if any, empty string if not.
89
- */
90
- private function get_content_type() {
91
- $headers = headers_list();
92
- foreach ( $headers as $header ) {
93
- if ( stripos( $header, 'Content-Type:' ) !== false ) {
94
- return (string) preg_replace( '/^Content-Type:\s*(.*)/', '$1', $header );
95
- }
96
- }
97
-
98
- return '';
99
- }
100
-
101
- /**
102
- * Outputs a log header.
103
- *
104
- * @param string $string The header to output.
105
- *
106
- * @return void
107
- */
108
- private function header( $string ) {
109
- echo PHP_EOL, PHP_EOL, $string, PHP_EOL;
110
- echo '====', PHP_EOL;
111
- }
112
-
113
- /**
114
- * Logs the memory usage.
115
- *
116
- * @return void
117
- */
118
- protected function log_memory_usage() {
119
- $memory_used_peak = number_format( ( memory_get_peak_usage() / ( 1024 * 1024 ) ), 2 );
120
- $memory_used = number_format( ( memory_get_usage() / ( 1024 * 1024 ) ), 2 );
121
-
122
- $this->header( 'Memory usage' );
123
- echo 'Peak: ', $memory_used_peak, 'MB', PHP_EOL;
124
- echo 'Average: ', $memory_used, 'MB', PHP_EOL;
125
- }
126
-
127
- /**
128
- * Logs the IdiORM queries.
129
- *
130
- * @return void
131
- */
132
- protected function log_idiorm_queries() {
133
- $this->header( 'Yoast Idiorm Queries (' . count( $this->query_log ) . ')' );
134
- $i = 1;
135
- foreach ( $this->query_log as $query ) {
136
- echo $i, ': "', $query['query'], '" in ', round( $query['time'], 5 ), PHP_EOL;
137
- $i ++;
138
- }
139
- }
140
-
141
- /**
142
- * Logs the WPDB queries, if `SAVEQUERIES` has been set to true.
143
- *
144
- * @return void
145
- */
146
- protected function log_wpdb_queries() {
147
- if ( defined( 'SAVEQUERIES' ) && SAVEQUERIES ) {
148
- global $wpdb;
149
- $this->header( 'WPDB Queries (' . count( $wpdb->queries ) . ')' );
150
- $i = 1;
151
- foreach ( $wpdb->queries as $query ) {
152
- echo $i, ': "', trim( $query[0] ), '" in ', round( $query[1], 5 ), PHP_EOL;
153
- echo ' ', $query[2], PHP_EOL;
154
- $i ++;
155
- }
156
-
157
- return;
158
- }
159
- $this->header( 'WPDB Queries' );
160
- echo 'Please add this to your wp-config.php to allow WPDB Query logging:', PHP_EOL;
161
- echo "define( 'SAVEQUERIES', true );", PHP_EOL;
162
- }
163
-
164
- /**
165
- * Logs the request time.
166
- *
167
- * @return void
168
- */
169
- protected function log_time() {
170
- $this->header( 'Request time' );
171
- echo timer_stop(), 's', PHP_EOL;
172
- }
173
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/models/indexable-extension.php CHANGED
@@ -7,12 +7,12 @@
7
 
8
  namespace Yoast\WP\SEO\Models;
9
 
10
- use Yoast\WP\SEO\ORM\Yoast_Model;
11
 
12
  /**
13
  * Abstract class for indexable extensions.
14
  */
15
- abstract class Indexable_Extension extends Yoast_Model {
16
 
17
  /**
18
  * Holds the Indexable instance.
7
 
8
  namespace Yoast\WP\SEO\Models;
9
 
10
+ use Yoast\WP\Lib\Model;
11
 
12
  /**
13
  * Abstract class for indexable extensions.
14
  */
15
+ abstract class Indexable_Extension extends Model {
16
 
17
  /**
18
  * Holds the Indexable instance.
src/models/indexable-hierarchy.php CHANGED
@@ -7,7 +7,7 @@
7
 
8
  namespace Yoast\WP\SEO\Models;
9
 
10
- use Yoast\WP\SEO\ORM\Yoast_Model;
11
 
12
  /**
13
  * Indexable Hierarchy model definition.
@@ -17,7 +17,7 @@ use Yoast\WP\SEO\ORM\Yoast_Model;
17
  * @property int $depth The depth of the ancestry. 1 being a parent, 2 being a grandparent etc.
18
  * @property int $blog_id Blog ID.
19
  */
20
- class Indexable_Hierarchy extends Yoast_Model {
21
 
22
  /**
23
  * Which columns contain int values.
7
 
8
  namespace Yoast\WP\SEO\Models;
9
 
10
+ use Yoast\WP\Lib\Model;
11
 
12
  /**
13
  * Indexable Hierarchy model definition.
17
  * @property int $depth The depth of the ancestry. 1 being a parent, 2 being a grandparent etc.
18
  * @property int $blog_id Blog ID.
19
  */
20
+ class Indexable_Hierarchy extends Model {
21
 
22
  /**
23
  * Which columns contain int values.
src/models/indexable.php CHANGED
@@ -7,7 +7,7 @@
7
 
8
  namespace Yoast\WP\SEO\Models;
9
 
10
- use Yoast\WP\SEO\ORM\Yoast_Model;
11
 
12
  /**
13
  * Indexable table definition.
@@ -78,7 +78,7 @@ use Yoast\WP\SEO\ORM\Yoast_Model;
78
  * @property string $schema_page_type
79
  * @property string $schema_article_type
80
  */
81
- class Indexable extends Yoast_Model {
82
 
83
  /**
84
  * Whether nor this model uses timestamps.
@@ -153,9 +153,15 @@ class Indexable extends Yoast_Model {
153
  */
154
  public function save() {
155
  if ( $this->permalink ) {
156
- $this->permalink = \trailingslashit( $this->permalink );
 
 
 
157
  $this->permalink_hash = \strlen( $this->permalink ) . ':' . \md5( $this->permalink );
158
  }
 
 
 
159
 
160
  return parent::save();
161
  }
7
 
8
  namespace Yoast\WP\SEO\Models;
9
 
10
+ use Yoast\WP\Lib\Model;
11
 
12
  /**
13
  * Indexable table definition.
78
  * @property string $schema_page_type
79
  * @property string $schema_article_type
80
  */
81
+ class Indexable extends Model {
82
 
83
  /**
84
  * Whether nor this model uses timestamps.
153
  */
154
  public function save() {
155
  if ( $this->permalink ) {
156
+ $permalink_structure = get_option( 'permalink_structure' );
157
+ if ( substr( $permalink_structure , -1, 1 ) === '/' ) {
158
+ $this->permalink = \trailingslashit( $this->permalink );
159
+ }
160
  $this->permalink_hash = \strlen( $this->permalink ) . ':' . \md5( $this->permalink );
161
  }
162
+ if ( \strlen( $this->primary_focus_keyword ) > 191 ) {
163
+ $this->primary_focus_keyword = substr( $this->primary_focus_keyword, 0, 191 );
164
+ }
165
 
166
  return parent::save();
167
  }
src/models/primary-term.php CHANGED
@@ -7,7 +7,7 @@
7
 
8
  namespace Yoast\WP\SEO\Models;
9
 
10
- use Yoast\WP\SEO\ORM\Yoast_Model;
11
 
12
  /**
13
  * Primary Term model definition.
@@ -21,7 +21,7 @@ use Yoast\WP\SEO\ORM\Yoast_Model;
21
  * @property string $created_at
22
  * @property string $updated_at
23
  */
24
- class Primary_Term extends Yoast_Model {
25
 
26
  /**
27
  * Whether nor this model uses timestamps.
7
 
8
  namespace Yoast\WP\SEO\Models;
9
 
10
+ use Yoast\WP\Lib\Model;
11
 
12
  /**
13
  * Primary Term model definition.
21
  * @property string $created_at
22
  * @property string $updated_at
23
  */
24
+ class Primary_Term extends Model {
25
 
26
  /**
27
  * Whether nor this model uses timestamps.
src/models/seo-links.php CHANGED
@@ -7,7 +7,7 @@
7
 
8
  namespace Yoast\WP\SEO\Models;
9
 
10
- use Yoast\WP\SEO\ORM\Yoast_Model;
11
 
12
  /**
13
  * Table definition for the SEO Meta table.
@@ -18,7 +18,7 @@ use Yoast\WP\SEO\ORM\Yoast_Model;
18
  * @property int $target_post_id
19
  * @property string $type
20
  */
21
- class SEO_Links extends Yoast_Model {
22
 
23
  /**
24
  * Which columns contain int values.
7
 
8
  namespace Yoast\WP\SEO\Models;
9
 
10
+ use Yoast\WP\Lib\Model;
11
 
12
  /**
13
  * Table definition for the SEO Meta table.
18
  * @property int $target_post_id
19
  * @property string $type
20
  */
21
+ class SEO_Links extends Model {
22
 
23
  /**
24
  * Which columns contain int values.
src/models/seo-meta.php CHANGED
@@ -7,7 +7,7 @@
7
 
8
  namespace Yoast\WP\SEO\Models;
9
 
10
- use Yoast\WP\SEO\ORM\Yoast_Model;
11
 
12
  /**
13
  * Table definition for the SEO Meta table.
@@ -16,7 +16,7 @@ use Yoast\WP\SEO\ORM\Yoast_Model;
16
  * @property int $internal_link_count
17
  * @property int $incoming_link_count
18
  */
19
- class SEO_Meta extends Yoast_Model {
20
 
21
  /**
22
  * Overwrites the default ID column name.
7
 
8
  namespace Yoast\WP\SEO\Models;
9
 
10
+ use Yoast\WP\Lib\Model;
11
 
12
  /**
13
  * Table definition for the SEO Meta table.
16
  * @property int $internal_link_count
17
  * @property int $incoming_link_count
18
  */
19
+ class SEO_Meta extends Model {
20
 
21
  /**
22
  * Overwrites the default ID column name.
src/orm/yoast-orm-wrapper.php DELETED
@@ -1,234 +0,0 @@
1
- <?php
2
- /**
3
- * Yoast extension of the ORM class.
4
- *
5
- * @package Yoast\YoastSEO
6
- */
7
-
8
- namespace Yoast\WP\SEO\ORM;
9
-
10
- use Exception;
11
- use PDO;
12
- use Yoast\WP\Polyfills\PDO\PDO_MySQLi_Polyfill;
13
- use Yoast\WP\SEO\Initializers\Migration_Runner;
14
- use YoastSEO_Vendor\ORM;
15
-
16
- /**
17
- * Subclass of Idiorm's ORM class that supports
18
- * returning instances of a specified class rather
19
- * than raw instances of the ORM class.
20
- *
21
- * You shouldn't need to interact with this class
22
- * directly. It is used internally by the Model base
23
- * class.
24
- *
25
- * The methods documented below are magic methods that conform to PSR-1.
26
- * This documentation exposes these methods to doc generators and IDEs.
27
- *
28
- * @link http://www.php-fig.org/psr/psr-1/
29
- *
30
- * @method void setClassName($class_name)
31
- * @method static \Yoast\WP\SEO\ORM\ORMWrapper forTable($table_name, $connection_name = parent::DEFAULT_CONNECTION)
32
- * @method \Yoast\WP\SEO\ORM\\Model findOne($id=null)
33
- * @method Array|\IdiormResultSet findMany()
34
- */
35
- class ORMWrapper extends ORM {
36
-
37
- /**
38
- * Contains the repositories.
39
- *
40
- * @var array
41
- */
42
- public static $repositories = [];
43
-
44
- /**
45
- * The wrapped find_one and find_many classes will return an instance or
46
- * instances of this class.
47
- *
48
- * @var string
49
- */
50
- protected $class_name;
51
-
52
- /**
53
- * Set the name of the class which the wrapped methods should return
54
- * instances of.
55
- *
56
- * @param string $class_name The classname to set.
57
- *
58
- * @return void
59
- */
60
- public function set_class_name( $class_name ) {
61
- $this->class_name = $class_name;
62
- }
63
-
64
- /**
65
- * Add a custom filter to the method chain specified on the model class.
66
- * This allows custom queries to be added to models. The filter should take
67
- * an instance of the ORM wrapper as its first argument and return an
68
- * instance of the ORM wrapper. Any arguments passed to this method after
69
- * the name of the filter will be passed to the called filter function as
70
- * arguments after the ORM class.
71
- *
72
- * @return \Yoast\WP\SEO\ORM\ORMWrapper Instance of the ORM wrapper.
73
- */
74
- public function filter() {
75
- $args = \func_get_args();
76
- $filter_function = \array_shift( $args );
77
- \array_unshift( $args, $this );
78
- if ( \method_exists( $this->class_name, $filter_function ) ) {
79
- return \call_user_func_array( [ $this->class_name, $filter_function ], $args );
80
- }
81
-
82
- return null;
83
- }
84
-
85
- /**
86
- * Factory method, return an instance of this class bound to the supplied
87
- * table name.
88
- *
89
- * A repeat of content in parent::for_table, so that created class is
90
- * ORMWrapper, not ORM.
91
- *
92
- * @param string $table_name The table to create instance for.
93
- * @param string $connection_name The connection name.
94
- *
95
- * @return \Yoast\WP\SEO\ORM\ORMWrapper Instance of the ORM wrapper.
96
- */
97
- public static function for_table( $table_name, $connection_name = parent::DEFAULT_CONNECTION ) {
98
- static::_setup_db( $connection_name );
99
- return new static( $table_name, [], $connection_name );
100
- }
101
-
102
- /**
103
- * Method to create an instance of the model class associated with this
104
- * wrapper and populate it with the supplied Idiorm instance.
105
- *
106
- * @param \Yoast\WP\SEO\ORM\ORMWrapper|\YoastSEO_Vendor\ORM $orm The ORM used by model.
107
- *
108
- * @return bool|\Yoast\WP\SEO\ORM\Yoast_Model Instance of the model class.
109
- */
110
- protected function create_model_instance( $orm ) {
111
- if ( $orm === false ) {
112
- return false;
113
- }
114
-
115
- /**
116
- * An instance of Yoast_Model is being made.
117
- *
118
- * @var \Yoast\WP\SEO\ORM\Yoast_Model $model
119
- */
120
- $model = new $this->class_name();
121
- $model->set_orm( $orm );
122
-
123
- return $model;
124
- }
125
-
126
- /**
127
- * Wrap Idiorm's find_one method to return an instance of the class
128
- * associated with this wrapper instead of the raw ORM class.
129
- *
130
- * @param null|integer $id The ID to lookup.
131
- *
132
- * @return \Yoast\WP\SEO\ORM\Yoast_Model Instance of the model.
133
- */
134
- public function find_one( $id = null ) {
135
- return $this->create_model_instance( parent::find_one( $id ) );
136
- }
137
-
138
- /**
139
- * Wrap Idiorm's find_many method to return an array of instances of the
140
- * class associated with this wrapper instead of the raw ORM class.
141
- *
142
- * @return array The found results.
143
- */
144
- public function find_many() {
145
- $results = parent::find_many();
146
- foreach ( $results as $key => $result ) {
147
- $results[ $key ] = $this->create_model_instance( $result );
148
- }
149
-
150
- return $results;
151
- }
152
-
153
- /**
154
- * Wrap Idiorm's create method to return an empty instance of the class
155
- * associated with this wrapper instead of the raw ORM class.
156
- *
157
- * @param null|mixed $data The data to pass.
158
- *
159
- * @return \Yoast\WP\SEO\ORM\Yoast_Model|bool Instance of the ORM.
160
- */
161
- public function create( $data = null ) {
162
- return $this->create_model_instance( parent::create( $data ) );
163
- }
164
-
165
- /**
166
- * Returns the select query as SQL.
167
- *
168
- * @return string The select query in SQL.
169
- */
170
- public function get_sql() {
171
- return $this->_build_select();
172
- }
173
-
174
- /**
175
- * Returns the update query as SQL.
176
- *
177
- * @return string The update query in SQL.
178
- */
179
- public function get_update_sql() {
180
- return $this->_build_update();
181
- }
182
-
183
- /**
184
- * Set up the database connection used by the class
185
- *
186
- * @param string $connection_name Which connection to use.
187
- */
188
- protected static function _setup_db( $connection_name = self::DEFAULT_CONNECTION ) {
189
- if (
190
- ! array_key_exists( $connection_name, self::$_db ) ||
191
- ! is_object( self::$_db[ $connection_name ] )
192
- ) {
193
- self::_setup_db_config( $connection_name );
194
-
195
- if ( extension_loaded( 'pdo_mysql' ) ) {
196
- // @codingStandardsIgnoreStart -- Reason: This is part of a well-tested library.
197
- $db = new PDO(
198
- self::$_config[ $connection_name ]['connection_string'],
199
- self::$_config[ $connection_name ]['username'],
200
- self::$_config[ $connection_name ]['password'],
201
- self::$_config[ $connection_name ]['driver_options']
202
- );
203
- $db->setAttribute( PDO::ATTR_ERRMODE, self::$_config[ $connection_name ]['error_mode'] );
204
- // @codingStandardsIgnoreEnd -- Reason: This is part of a well-tested library.
205
- }
206
- else {
207
- $db = new PDO_MySQLi_Polyfill(
208
- self::$_config[ $connection_name ]['connection_string'],
209
- self::$_config[ $connection_name ]['username'],
210
- self::$_config[ $connection_name ]['password'],
211
- self::$_config[ $connection_name ]['driver_options']
212
- );
213
- }
214
-
215
- self::set_db( $db, $connection_name );
216
- }
217
- }
218
-
219
- /**
220
- * Execute the SELECT query that has been built up by chaining methods
221
- * on this class. Return an array of rows as associative arrays.
222
- */
223
- protected function _run() {
224
- try {
225
- return parent::_run();
226
- } catch ( Exception $exception ) {
227
- // If the query fails run the migrations and try again.
228
- // Action is intentionally undocumented and should not be used by third-parties.
229
- \do_action( '_yoast_run_migrations' );
230
- $this->_reset_idiorm_state();
231
- return parent::_run();
232
- }
233
- }
234
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/presenters/admin/indexation-warning-presenter.php CHANGED
@@ -21,7 +21,7 @@ class Indexation_Warning_Presenter extends Abstract_Presenter {
21
  */
22
  public function present() {
23
  return \sprintf(
24
- '<div id="yoast-indexation-warning" class="notice notice-success"><p>%1$s</p>%2$s<p>%3$s</p></div>',
25
  \sprintf(
26
  /* translators: 1: Strong start tag, 2: Strong closing tag, 3: Yoast SEO. */
27
  \esc_html__( '%1$sNEW:%2$s %3$s can now store your site’s SEO data in a smarter way!', 'wordpress-seo' ),
@@ -29,6 +29,7 @@ class Indexation_Warning_Presenter extends Abstract_Presenter {
29
  '</strong>',
30
  'Yoast SEO'
31
  ),
 
32
  \sprintf(
33
  /* translators: 1: Button start tag to open the indexation modal, 2: Button closing tag. */
34
  \esc_html__( '%1$sClick here to speed up your site now%2$s', 'wordpress-seo' ),
21
  */
22
  public function present() {
23
  return \sprintf(
24
+ '<div id="yoast-indexation-warning" class="notice notice-success"><p>%1$s<br/>%2$s</p>%3$s<p>%4$s</p></div>',
25
  \sprintf(
26
  /* translators: 1: Strong start tag, 2: Strong closing tag, 3: Yoast SEO. */
27
  \esc_html__( '%1$sNEW:%2$s %3$s can now store your site’s SEO data in a smarter way!', 'wordpress-seo' ),
29
  '</strong>',
30
  'Yoast SEO'
31
  ),
32
+ \esc_html__( 'Don\'t worry: this won\'t have to be done after each update.', 'wordpress-seo' ),
33
  \sprintf(
34
  /* translators: 1: Button start tag to open the indexation modal, 2: Button closing tag. */
35
  \esc_html__( '%1$sClick here to speed up your site now%2$s', 'wordpress-seo' ),
src/repositories/indexable-hierarchy-repository.php CHANGED
@@ -10,7 +10,7 @@ namespace Yoast\WP\SEO\Repositories;
10
  use Yoast\WP\SEO\Builders\Indexable_Hierarchy_Builder;
11
  use Yoast\WP\SEO\Models\Indexable;
12
  use Yoast\WP\SEO\Models\Indexable_Hierarchy;
13
- use Yoast\WP\SEO\ORM\Yoast_Model;
14
 
15
  /**
16
  * Class Indexable_Hierarchy_Repository
@@ -55,7 +55,7 @@ class Indexable_Hierarchy_Repository {
55
  * @param int $ancestor_id The ancestor id.
56
  * @param int $depth The depth.
57
  *
58
- * @return bool|Yoast_Model
59
  */
60
  public function add_ancestor( $indexable_id, $ancestor_id, $depth ) {
61
  $hierarchy = $this->query()->create( [
@@ -101,6 +101,6 @@ class Indexable_Hierarchy_Repository {
101
  * @return \Yoast\WP\SEO\ORM\ORMWrapper
102
  */
103
  public function query() {
104
- return Yoast_Model::of_type( 'Indexable_Hierarchy' );
105
  }
106
  }
10
  use Yoast\WP\SEO\Builders\Indexable_Hierarchy_Builder;
11
  use Yoast\WP\SEO\Models\Indexable;
12
  use Yoast\WP\SEO\Models\Indexable_Hierarchy;
13
+ use Yoast\WP\Lib\Model;
14
 
15
  /**
16
  * Class Indexable_Hierarchy_Repository
55
  * @param int $ancestor_id The ancestor id.
56
  * @param int $depth The depth.
57
  *
58
+ * @return bool|Model
59
  */
60
  public function add_ancestor( $indexable_id, $ancestor_id, $depth ) {
61
  $hierarchy = $this->query()->create( [
101
  * @return \Yoast\WP\SEO\ORM\ORMWrapper
102
  */
103
  public function query() {
104
+ return Model::of_type( 'Indexable_Hierarchy' );
105
  }
106
  }
src/repositories/indexable-repository.php CHANGED
@@ -13,7 +13,7 @@ use Yoast\WP\SEO\Helpers\Current_Page_Helper;
13
  use Yoast\WP\SEO\Loggers\Logger;
14
  use Yoast\WP\SEO\Models\Indexable;
15
  use Yoast\WP\SEO\ORM\ORMWrapper;
16
- use Yoast\WP\SEO\ORM\Yoast_Model;
17
 
18
  /**
19
  * Class Indexable_Repository
@@ -77,7 +77,7 @@ class Indexable_Repository {
77
  * @return ORMWrapper
78
  */
79
  public function query() {
80
- return Yoast_Model::of_type( 'Indexable' );
81
  }
82
 
83
  /**
@@ -121,7 +121,7 @@ class Indexable_Repository {
121
  }
122
 
123
  if ( $indexable === false ) {
124
- return $this->query()->create( [ 'object_type' => 'unknown' ] );
125
  }
126
 
127
  return $indexable;
@@ -335,14 +335,7 @@ class Indexable_Repository {
335
  $indexables_to_create = \array_diff( $object_ids, $indexables_available );
336
 
337
  foreach ( $indexables_to_create as $indexable_to_create ) {
338
- $indexable = $this->builder->build_for_id_and_type( $indexable_to_create, $object_type );
339
- if ( $indexable === false ) {
340
- continue;
341
- }
342
-
343
- $indexable->save();
344
-
345
- $indexables[] = $indexable;
346
  }
347
  }
348
 
13
  use Yoast\WP\SEO\Loggers\Logger;
14
  use Yoast\WP\SEO\Models\Indexable;
15
  use Yoast\WP\SEO\ORM\ORMWrapper;
16
+ use Yoast\WP\Lib\Model;
17
 
18
  /**
19
  * Class Indexable_Repository
77
  * @return ORMWrapper
78
  */
79
  public function query() {
80
+ return Model::of_type( 'Indexable' );
81
  }
82
 
83
  /**
121
  }
122
 
123
  if ( $indexable === false ) {
124
+ return $this->query()->create( [ 'object_type' => 'unknown', 'post_status' => 'unindexed' ] );
125
  }
126
 
127
  return $indexable;
335
  $indexables_to_create = \array_diff( $object_ids, $indexables_available );
336
 
337
  foreach ( $indexables_to_create as $indexable_to_create ) {
338
+ $indexables[] = $this->builder->build_for_id_and_type( $indexable_to_create, $object_type );
 
 
 
 
 
 
 
339
  }
340
  }
341
 
src/repositories/primary-term-repository.php CHANGED
@@ -9,7 +9,7 @@ namespace Yoast\WP\SEO\Repositories;
9
 
10
  use Yoast\WP\SEO\Models\Primary_Term;
11
  use Yoast\WP\SEO\ORM\ORMWrapper;
12
- use Yoast\WP\SEO\ORM\Yoast_Model;
13
 
14
  /**
15
  * Class Primary_Term_Repository
@@ -24,7 +24,7 @@ class Primary_Term_Repository {
24
  * @return ORMWrapper
25
  */
26
  public function query() {
27
- return Yoast_Model::of_type( 'Primary_Term' );
28
  }
29
 
30
  /**
9
 
10
  use Yoast\WP\SEO\Models\Primary_Term;
11
  use Yoast\WP\SEO\ORM\ORMWrapper;
12
+ use Yoast\WP\Lib\Model;
13
 
14
  /**
15
  * Class Primary_Term_Repository
24
  * @return ORMWrapper
25
  */
26
  public function query() {
27
+ return Model::of_type( 'Primary_Term' );
28
  }
29
 
30
  /**
src/repositories/seo-links-repository.php CHANGED
@@ -8,7 +8,7 @@
8
  namespace Yoast\WP\SEO\Repositories;
9
 
10
  use Yoast\WP\SEO\ORM\ORMWrapper;
11
- use Yoast\WP\SEO\ORM\Yoast_Model;
12
 
13
  /**
14
  * Class SEO_Links_Repository
@@ -23,6 +23,6 @@ class SEO_Links_Repository {
23
  * @return ORMWrapper
24
  */
25
  public function query() {
26
- return Yoast_Model::of_type( 'SEO_Links' );
27
  }
28
  }
8
  namespace Yoast\WP\SEO\Repositories;
9
 
10
  use Yoast\WP\SEO\ORM\ORMWrapper;
11
+ use Yoast\WP\Lib\Model;
12
 
13
  /**
14
  * Class SEO_Links_Repository
23
  * @return ORMWrapper
24
  */
25
  public function query() {
26
+ return Model::of_type( 'SEO_Links' );
27
  }
28
  }
src/repositories/seo-meta-repository.php CHANGED
@@ -8,7 +8,7 @@
8
  namespace Yoast\WP\SEO\Repositories;
9
 
10
  use Yoast\WP\SEO\ORM\ORMWrapper;
11
- use Yoast\WP\SEO\ORM\Yoast_Model;
12
 
13
  /**
14
  * Class SEO_Meta_Repository
@@ -23,7 +23,7 @@ class SEO_Meta_Repository {
23
  * @return ORMWrapper
24
  */
25
  public function query() {
26
- return Yoast_Model::of_type( 'SEO_Meta' );
27
  }
28
 
29
  /**
8
  namespace Yoast\WP\SEO\Repositories;
9
 
10
  use Yoast\WP\SEO\ORM\ORMWrapper;
11
+ use Yoast\WP\Lib\Model;
12
 
13
  /**
14
  * Class SEO_Meta_Repository
23
  * @return ORMWrapper
24
  */
25
  public function query() {
26
+ return Model::of_type( 'SEO_Meta' );
27
  }
28
 
29
  /**
vendor/composer/autoload_classmap.php CHANGED
@@ -125,7 +125,7 @@ return array(
125
  'WPSEO_Advanced_Settings' => $baseDir . '/deprecated/class-wpseo-advanced-settings.php',
126
  'WPSEO_Author_Sitemap_Provider' => $baseDir . '/inc/sitemaps/class-author-sitemap-provider.php',
127
  'WPSEO_Base_Menu' => $baseDir . '/admin/menu/class-base-menu.php',
128
- 'WPSEO_Breadcrumbs' => $baseDir . '/src/backwards-compatibility/breadcrumbs.php',
129
  'WPSEO_Bulk_Description_List_Table' => $baseDir . '/admin/class-bulk-description-editor-list-table.php',
130
  'WPSEO_Bulk_List_Table' => $baseDir . '/admin/class-bulk-editor-list-table.php',
131
  'WPSEO_Bulk_Title_Editor_List_Table' => $baseDir . '/admin/class-bulk-title-editor-list-table.php',
@@ -210,7 +210,7 @@ return array(
210
  'WPSEO_Features' => $baseDir . '/inc/class-wpseo-features.php',
211
  'WPSEO_File_Size_Exception' => $baseDir . '/admin/exceptions/class-file-size-exception.php',
212
  'WPSEO_File_Size_Service' => $baseDir . '/admin/services/class-file-size.php',
213
- 'WPSEO_Frontend' => $baseDir . '/src/backwards-compatibility/frontend.php',
214
  'WPSEO_Frontend_Page_Type' => $baseDir . '/frontend/class-frontend-page-type.php',
215
  'WPSEO_Frontend_Primary_Category' => $baseDir . '/deprecated/frontend/class-primary-category.php',
216
  'WPSEO_GSC' => $baseDir . '/admin/google_search_console/class-gsc.php',
@@ -621,6 +621,10 @@ return array(
621
  'YoastSEO_Vendor\\Task_Db_Setup' => $baseDir . '/vendor_prefixed/ruckusing/lib/Task/Db/Setup.php',
622
  'YoastSEO_Vendor\\Task_Db_Status' => $baseDir . '/vendor_prefixed/ruckusing/lib/Task/Db/Status.php',
623
  'YoastSEO_Vendor\\Task_Db_Version' => $baseDir . '/vendor_prefixed/ruckusing/lib/Task/Db/Version.php',
 
 
 
 
624
  'Yoast\\WP\\SEO\\Actions\\Indexables\\Indexable_Head_Action' => $baseDir . '/src/actions/indexables/indexable-head-action.php',
625
  'Yoast\\WP\\SEO\\Actions\\Indexation\\Indexable_General_Indexation_Action' => $baseDir . '/src/actions/indexation/indexable-general-indexation-action.php',
626
  'Yoast\\WP\\SEO\\Actions\\Indexation\\Indexable_Post_Indexation_Action' => $baseDir . '/src/actions/indexation/indexable-post-indexation-action.php',
@@ -707,7 +711,6 @@ return array(
707
  'Yoast\\WP\\SEO\\Helpers\\Twitter\\Image_Helper' => $baseDir . '/src/helpers/twitter/image-helper.php',
708
  'Yoast\\WP\\SEO\\Helpers\\Url_Helper' => $baseDir . '/src/helpers/url-helper.php',
709
  'Yoast\\WP\\SEO\\Helpers\\User_Helper' => $baseDir . '/src/helpers/user-helper.php',
710
- 'Yoast\\WP\\SEO\\Initializers\\Database_Setup' => $baseDir . '/src/initializers/database-setup.php',
711
  'Yoast\\WP\\SEO\\Initializers\\Initializer_Interface' => $baseDir . '/src/initializers/initializer-interface.php',
712
  'Yoast\\WP\\SEO\\Initializers\\Migration_Runner' => $baseDir . '/src/initializers/migration-runner.php',
713
  'Yoast\\WP\\SEO\\Integrations\\Admin\\Indexation_Integration' => $baseDir . '/src/integrations/admin/indexation-integration.php',
@@ -745,7 +748,6 @@ return array(
745
  'Yoast\\WP\\SEO\\Integrations\\Watchers\\Primary_Term_Watcher' => $baseDir . '/src/integrations/watchers/primary-term-watcher.php',
746
  'Yoast\\WP\\SEO\\Loadable_Interface' => $baseDir . '/src/loadable-interface.php',
747
  'Yoast\\WP\\SEO\\Loader' => $baseDir . '/src/loader.php',
748
- 'Yoast\\WP\\SEO\\Loggers\\Database_Logger' => $baseDir . '/src/loggers/database-logger.php',
749
  'Yoast\\WP\\SEO\\Loggers\\Logger' => $baseDir . '/src/loggers/logger.php',
750
  'Yoast\\WP\\SEO\\Loggers\\Migration_Logger' => $baseDir . '/src/loggers/migration-logger.php',
751
  'Yoast\\WP\\SEO\\Main' => $baseDir . '/src/main.php',
@@ -757,8 +759,6 @@ return array(
757
  'Yoast\\WP\\SEO\\Models\\Primary_Term' => $baseDir . '/src/models/primary-term.php',
758
  'Yoast\\WP\\SEO\\Models\\SEO_Links' => $baseDir . '/src/models/seo-links.php',
759
  'Yoast\\WP\\SEO\\Models\\SEO_Meta' => $baseDir . '/src/models/seo-meta.php',
760
- 'Yoast\\WP\\SEO\\ORM\\ORMWrapper' => $baseDir . '/src/orm/yoast-orm-wrapper.php',
761
- 'Yoast\\WP\\SEO\\ORM\\Yoast_Model' => $baseDir . '/src/orm/yoast-model.php',
762
  'Yoast\\WP\\SEO\\Oauth\\Client' => $baseDir . '/src/oauth/client.php',
763
  'Yoast\\WP\\SEO\\Presentations\\Abstract_Presentation' => $baseDir . '/src/presentations/abstract-presentation.php',
764
  'Yoast\\WP\\SEO\\Presentations\\Archive_Adjacent' => $baseDir . '/src/presentations/archive-adjacent.php',
125
  'WPSEO_Advanced_Settings' => $baseDir . '/deprecated/class-wpseo-advanced-settings.php',
126
  'WPSEO_Author_Sitemap_Provider' => $baseDir . '/inc/sitemaps/class-author-sitemap-provider.php',
127
  'WPSEO_Base_Menu' => $baseDir . '/admin/menu/class-base-menu.php',
128
+ 'WPSEO_Breadcrumbs' => $baseDir . '/deprecated/frontend/breadcrumbs.php',
129
  'WPSEO_Bulk_Description_List_Table' => $baseDir . '/admin/class-bulk-description-editor-list-table.php',
130
  'WPSEO_Bulk_List_Table' => $baseDir . '/admin/class-bulk-editor-list-table.php',
131
  'WPSEO_Bulk_Title_Editor_List_Table' => $baseDir . '/admin/class-bulk-title-editor-list-table.php',
210
  'WPSEO_Features' => $baseDir . '/inc/class-wpseo-features.php',
211
  'WPSEO_File_Size_Exception' => $baseDir . '/admin/exceptions/class-file-size-exception.php',
212
  'WPSEO_File_Size_Service' => $baseDir . '/admin/services/class-file-size.php',
213
+ 'WPSEO_Frontend' => $baseDir . '/deprecated/frontend/frontend.php',
214
  'WPSEO_Frontend_Page_Type' => $baseDir . '/frontend/class-frontend-page-type.php',
215
  'WPSEO_Frontend_Primary_Category' => $baseDir . '/deprecated/frontend/class-primary-category.php',
216
  'WPSEO_GSC' => $baseDir . '/admin/google_search_console/class-gsc.php',
621
  'YoastSEO_Vendor\\Task_Db_Setup' => $baseDir . '/vendor_prefixed/ruckusing/lib/Task/Db/Setup.php',
622
  'YoastSEO_Vendor\\Task_Db_Status' => $baseDir . '/vendor_prefixed/ruckusing/lib/Task/Db/Status.php',
623
  'YoastSEO_Vendor\\Task_Db_Version' => $baseDir . '/vendor_prefixed/ruckusing/lib/Task/Db/Version.php',
624
+ 'Yoast\\WP\\Lib\\Model' => $baseDir . '/lib/model.php',
625
+ 'Yoast\\WP\\Lib\\ORM' => $baseDir . '/lib/orm.php',
626
+ 'Yoast\\WP\\Lib\\Ruckusing_Adapter' => $baseDir . '/lib/ruckusing-adapter.php',
627
+ 'Yoast\\WP\\Lib\\Ruckusing_Framework_Runner' => $baseDir . '/lib/ruckusing-framework-runner.php',
628
  'Yoast\\WP\\SEO\\Actions\\Indexables\\Indexable_Head_Action' => $baseDir . '/src/actions/indexables/indexable-head-action.php',
629
  'Yoast\\WP\\SEO\\Actions\\Indexation\\Indexable_General_Indexation_Action' => $baseDir . '/src/actions/indexation/indexable-general-indexation-action.php',
630
  'Yoast\\WP\\SEO\\Actions\\Indexation\\Indexable_Post_Indexation_Action' => $baseDir . '/src/actions/indexation/indexable-post-indexation-action.php',
711
  'Yoast\\WP\\SEO\\Helpers\\Twitter\\Image_Helper' => $baseDir . '/src/helpers/twitter/image-helper.php',
712
  'Yoast\\WP\\SEO\\Helpers\\Url_Helper' => $baseDir . '/src/helpers/url-helper.php',
713
  'Yoast\\WP\\SEO\\Helpers\\User_Helper' => $baseDir . '/src/helpers/user-helper.php',
 
714
  'Yoast\\WP\\SEO\\Initializers\\Initializer_Interface' => $baseDir . '/src/initializers/initializer-interface.php',
715
  'Yoast\\WP\\SEO\\Initializers\\Migration_Runner' => $baseDir . '/src/initializers/migration-runner.php',
716
  'Yoast\\WP\\SEO\\Integrations\\Admin\\Indexation_Integration' => $baseDir . '/src/integrations/admin/indexation-integration.php',
748
  'Yoast\\WP\\SEO\\Integrations\\Watchers\\Primary_Term_Watcher' => $baseDir . '/src/integrations/watchers/primary-term-watcher.php',
749
  'Yoast\\WP\\SEO\\Loadable_Interface' => $baseDir . '/src/loadable-interface.php',
750
  'Yoast\\WP\\SEO\\Loader' => $baseDir . '/src/loader.php',
 
751
  'Yoast\\WP\\SEO\\Loggers\\Logger' => $baseDir . '/src/loggers/logger.php',
752
  'Yoast\\WP\\SEO\\Loggers\\Migration_Logger' => $baseDir . '/src/loggers/migration-logger.php',
753
  'Yoast\\WP\\SEO\\Main' => $baseDir . '/src/main.php',
759
  'Yoast\\WP\\SEO\\Models\\Primary_Term' => $baseDir . '/src/models/primary-term.php',
760
  'Yoast\\WP\\SEO\\Models\\SEO_Links' => $baseDir . '/src/models/seo-links.php',
761
  'Yoast\\WP\\SEO\\Models\\SEO_Meta' => $baseDir . '/src/models/seo-meta.php',
 
 
762
  'Yoast\\WP\\SEO\\Oauth\\Client' => $baseDir . '/src/oauth/client.php',
763
  'Yoast\\WP\\SEO\\Presentations\\Abstract_Presentation' => $baseDir . '/src/presentations/abstract-presentation.php',
764
  'Yoast\\WP\\SEO\\Presentations\\Archive_Adjacent' => $baseDir . '/src/presentations/archive-adjacent.php',
vendor/composer/autoload_static.php CHANGED
@@ -140,7 +140,7 @@ class ComposerStaticInit424d5d6160c52f59ba476750c190098f
140
  'WPSEO_Advanced_Settings' => __DIR__ . '/../..' . '/deprecated/class-wpseo-advanced-settings.php',
141
  'WPSEO_Author_Sitemap_Provider' => __DIR__ . '/../..' . '/inc/sitemaps/class-author-sitemap-provider.php',
142
  'WPSEO_Base_Menu' => __DIR__ . '/../..' . '/admin/menu/class-base-menu.php',
143
- 'WPSEO_Breadcrumbs' => __DIR__ . '/../..' . '/src/backwards-compatibility/breadcrumbs.php',
144
  'WPSEO_Bulk_Description_List_Table' => __DIR__ . '/../..' . '/admin/class-bulk-description-editor-list-table.php',
145
  'WPSEO_Bulk_List_Table' => __DIR__ . '/../..' . '/admin/class-bulk-editor-list-table.php',
146
  'WPSEO_Bulk_Title_Editor_List_Table' => __DIR__ . '/../..' . '/admin/class-bulk-title-editor-list-table.php',
@@ -225,7 +225,7 @@ class ComposerStaticInit424d5d6160c52f59ba476750c190098f
225
  'WPSEO_Features' => __DIR__ . '/../..' . '/inc/class-wpseo-features.php',
226
  'WPSEO_File_Size_Exception' => __DIR__ . '/../..' . '/admin/exceptions/class-file-size-exception.php',
227
  'WPSEO_File_Size_Service' => __DIR__ . '/../..' . '/admin/services/class-file-size.php',
228
- 'WPSEO_Frontend' => __DIR__ . '/../..' . '/src/backwards-compatibility/frontend.php',
229
  'WPSEO_Frontend_Page_Type' => __DIR__ . '/../..' . '/frontend/class-frontend-page-type.php',
230
  'WPSEO_Frontend_Primary_Category' => __DIR__ . '/../..' . '/deprecated/frontend/class-primary-category.php',
231
  'WPSEO_GSC' => __DIR__ . '/../..' . '/admin/google_search_console/class-gsc.php',
@@ -636,6 +636,10 @@ class ComposerStaticInit424d5d6160c52f59ba476750c190098f
636
  'YoastSEO_Vendor\\Task_Db_Setup' => __DIR__ . '/../..' . '/vendor_prefixed/ruckusing/lib/Task/Db/Setup.php',
637
  'YoastSEO_Vendor\\Task_Db_Status' => __DIR__ . '/../..' . '/vendor_prefixed/ruckusing/lib/Task/Db/Status.php',
638
  'YoastSEO_Vendor\\Task_Db_Version' => __DIR__ . '/../..' . '/vendor_prefixed/ruckusing/lib/Task/Db/Version.php',
 
 
 
 
639
  'Yoast\\WP\\SEO\\Actions\\Indexables\\Indexable_Head_Action' => __DIR__ . '/../..' . '/src/actions/indexables/indexable-head-action.php',
640
  'Yoast\\WP\\SEO\\Actions\\Indexation\\Indexable_General_Indexation_Action' => __DIR__ . '/../..' . '/src/actions/indexation/indexable-general-indexation-action.php',
641
  'Yoast\\WP\\SEO\\Actions\\Indexation\\Indexable_Post_Indexation_Action' => __DIR__ . '/../..' . '/src/actions/indexation/indexable-post-indexation-action.php',
@@ -722,7 +726,6 @@ class ComposerStaticInit424d5d6160c52f59ba476750c190098f
722
  'Yoast\\WP\\SEO\\Helpers\\Twitter\\Image_Helper' => __DIR__ . '/../..' . '/src/helpers/twitter/image-helper.php',
723
  'Yoast\\WP\\SEO\\Helpers\\Url_Helper' => __DIR__ . '/../..' . '/src/helpers/url-helper.php',
724
  'Yoast\\WP\\SEO\\Helpers\\User_Helper' => __DIR__ . '/../..' . '/src/helpers/user-helper.php',
725
- 'Yoast\\WP\\SEO\\Initializers\\Database_Setup' => __DIR__ . '/../..' . '/src/initializers/database-setup.php',
726
  'Yoast\\WP\\SEO\\Initializers\\Initializer_Interface' => __DIR__ . '/../..' . '/src/initializers/initializer-interface.php',
727
  'Yoast\\WP\\SEO\\Initializers\\Migration_Runner' => __DIR__ . '/../..' . '/src/initializers/migration-runner.php',
728
  'Yoast\\WP\\SEO\\Integrations\\Admin\\Indexation_Integration' => __DIR__ . '/../..' . '/src/integrations/admin/indexation-integration.php',
@@ -760,7 +763,6 @@ class ComposerStaticInit424d5d6160c52f59ba476750c190098f
760
  'Yoast\\WP\\SEO\\Integrations\\Watchers\\Primary_Term_Watcher' => __DIR__ . '/../..' . '/src/integrations/watchers/primary-term-watcher.php',
761
  'Yoast\\WP\\SEO\\Loadable_Interface' => __DIR__ . '/../..' . '/src/loadable-interface.php',
762
  'Yoast\\WP\\SEO\\Loader' => __DIR__ . '/../..' . '/src/loader.php',
763
- 'Yoast\\WP\\SEO\\Loggers\\Database_Logger' => __DIR__ . '/../..' . '/src/loggers/database-logger.php',
764
  'Yoast\\WP\\SEO\\Loggers\\Logger' => __DIR__ . '/../..' . '/src/loggers/logger.php',
765
  'Yoast\\WP\\SEO\\Loggers\\Migration_Logger' => __DIR__ . '/../..' . '/src/loggers/migration-logger.php',
766
  'Yoast\\WP\\SEO\\Main' => __DIR__ . '/../..' . '/src/main.php',
@@ -772,8 +774,6 @@ class ComposerStaticInit424d5d6160c52f59ba476750c190098f
772
  'Yoast\\WP\\SEO\\Models\\Primary_Term' => __DIR__ . '/../..' . '/src/models/primary-term.php',
773
  'Yoast\\WP\\SEO\\Models\\SEO_Links' => __DIR__ . '/../..' . '/src/models/seo-links.php',
774
  'Yoast\\WP\\SEO\\Models\\SEO_Meta' => __DIR__ . '/../..' . '/src/models/seo-meta.php',
775
- 'Yoast\\WP\\SEO\\ORM\\ORMWrapper' => __DIR__ . '/../..' . '/src/orm/yoast-orm-wrapper.php',
776
- 'Yoast\\WP\\SEO\\ORM\\Yoast_Model' => __DIR__ . '/../..' . '/src/orm/yoast-model.php',
777
  'Yoast\\WP\\SEO\\Oauth\\Client' => __DIR__ . '/../..' . '/src/oauth/client.php',
778
  'Yoast\\WP\\SEO\\Presentations\\Abstract_Presentation' => __DIR__ . '/../..' . '/src/presentations/abstract-presentation.php',
779
  'Yoast\\WP\\SEO\\Presentations\\Archive_Adjacent' => __DIR__ . '/../..' . '/src/presentations/archive-adjacent.php',
140
  'WPSEO_Advanced_Settings' => __DIR__ . '/../..' . '/deprecated/class-wpseo-advanced-settings.php',
141
  'WPSEO_Author_Sitemap_Provider' => __DIR__ . '/../..' . '/inc/sitemaps/class-author-sitemap-provider.php',
142
  'WPSEO_Base_Menu' => __DIR__ . '/../..' . '/admin/menu/class-base-menu.php',
143
+ 'WPSEO_Breadcrumbs' => __DIR__ . '/../..' . '/deprecated/frontend/breadcrumbs.php',
144
  'WPSEO_Bulk_Description_List_Table' => __DIR__ . '/../..' . '/admin/class-bulk-description-editor-list-table.php',
145
  'WPSEO_Bulk_List_Table' => __DIR__ . '/../..' . '/admin/class-bulk-editor-list-table.php',
146
  'WPSEO_Bulk_Title_Editor_List_Table' => __DIR__ . '/../..' . '/admin/class-bulk-title-editor-list-table.php',
225
  'WPSEO_Features' => __DIR__ . '/../..' . '/inc/class-wpseo-features.php',
226
  'WPSEO_File_Size_Exception' => __DIR__ . '/../..' . '/admin/exceptions/class-file-size-exception.php',
227
  'WPSEO_File_Size_Service' => __DIR__ . '/../..' . '/admin/services/class-file-size.php',
228
+ 'WPSEO_Frontend' => __DIR__ . '/../..' . '/deprecated/frontend/frontend.php',
229
  'WPSEO_Frontend_Page_Type' => __DIR__ . '/../..' . '/frontend/class-frontend-page-type.php',
230
  'WPSEO_Frontend_Primary_Category' => __DIR__ . '/../..' . '/deprecated/frontend/class-primary-category.php',
231
  'WPSEO_GSC' => __DIR__ . '/../..' . '/admin/google_search_console/class-gsc.php',
636
  'YoastSEO_Vendor\\Task_Db_Setup' => __DIR__ . '/../..' . '/vendor_prefixed/ruckusing/lib/Task/Db/Setup.php',
637
  'YoastSEO_Vendor\\Task_Db_Status' => __DIR__ . '/../..' . '/vendor_prefixed/ruckusing/lib/Task/Db/Status.php',
638
  'YoastSEO_Vendor\\Task_Db_Version' => __DIR__ . '/../..' . '/vendor_prefixed/ruckusing/lib/Task/Db/Version.php',
639
+ 'Yoast\\WP\\Lib\\Model' => __DIR__ . '/../..' . '/lib/model.php',
640
+ 'Yoast\\WP\\Lib\\ORM' => __DIR__ . '/../..' . '/lib/orm.php',
641
+ 'Yoast\\WP\\Lib\\Ruckusing_Adapter' => __DIR__ . '/../..' . '/lib/ruckusing-adapter.php',
642
+ 'Yoast\\WP\\Lib\\Ruckusing_Framework_Runner' => __DIR__ . '/../..' . '/lib/ruckusing-framework-runner.php',
643
  'Yoast\\WP\\SEO\\Actions\\Indexables\\Indexable_Head_Action' => __DIR__ . '/../..' . '/src/actions/indexables/indexable-head-action.php',
644
  'Yoast\\WP\\SEO\\Actions\\Indexation\\Indexable_General_Indexation_Action' => __DIR__ . '/../..' . '/src/actions/indexation/indexable-general-indexation-action.php',
645
  'Yoast\\WP\\SEO\\Actions\\Indexation\\Indexable_Post_Indexation_Action' => __DIR__ . '/../..' . '/src/actions/indexation/indexable-post-indexation-action.php',
726
  'Yoast\\WP\\SEO\\Helpers\\Twitter\\Image_Helper' => __DIR__ . '/../..' . '/src/helpers/twitter/image-helper.php',
727
  'Yoast\\WP\\SEO\\Helpers\\Url_Helper' => __DIR__ . '/../..' . '/src/helpers/url-helper.php',
728
  'Yoast\\WP\\SEO\\Helpers\\User_Helper' => __DIR__ . '/../..' . '/src/helpers/user-helper.php',
 
729
  'Yoast\\WP\\SEO\\Initializers\\Initializer_Interface' => __DIR__ . '/../..' . '/src/initializers/initializer-interface.php',
730
  'Yoast\\WP\\SEO\\Initializers\\Migration_Runner' => __DIR__ . '/../..' . '/src/initializers/migration-runner.php',
731
  'Yoast\\WP\\SEO\\Integrations\\Admin\\Indexation_Integration' => __DIR__ . '/../..' . '/src/integrations/admin/indexation-integration.php',
763
  'Yoast\\WP\\SEO\\Integrations\\Watchers\\Primary_Term_Watcher' => __DIR__ . '/../..' . '/src/integrations/watchers/primary-term-watcher.php',
764
  'Yoast\\WP\\SEO\\Loadable_Interface' => __DIR__ . '/../..' . '/src/loadable-interface.php',
765
  'Yoast\\WP\\SEO\\Loader' => __DIR__ . '/../..' . '/src/loader.php',
 
766
  'Yoast\\WP\\SEO\\Loggers\\Logger' => __DIR__ . '/../..' . '/src/loggers/logger.php',
767
  'Yoast\\WP\\SEO\\Loggers\\Migration_Logger' => __DIR__ . '/../..' . '/src/loggers/migration-logger.php',
768
  'Yoast\\WP\\SEO\\Main' => __DIR__ . '/../..' . '/src/main.php',
774
  'Yoast\\WP\\SEO\\Models\\Primary_Term' => __DIR__ . '/../..' . '/src/models/primary-term.php',
775
  'Yoast\\WP\\SEO\\Models\\SEO_Links' => __DIR__ . '/../..' . '/src/models/seo-links.php',
776
  'Yoast\\WP\\SEO\\Models\\SEO_Meta' => __DIR__ . '/../..' . '/src/models/seo-meta.php',
 
 
777
  'Yoast\\WP\\SEO\\Oauth\\Client' => __DIR__ . '/../..' . '/src/oauth/client.php',
778
  'Yoast\\WP\\SEO\\Presentations\\Abstract_Presentation' => __DIR__ . '/../..' . '/src/presentations/abstract-presentation.php',
779
  'Yoast\\WP\\SEO\\Presentations\\Archive_Adjacent' => __DIR__ . '/../..' . '/src/presentations/archive-adjacent.php',
wp-seo-main.php CHANGED
@@ -15,7 +15,7 @@ if ( ! function_exists( 'add_filter' ) ) {
15
  * {@internal Nobody should be able to overrule the real version number as this can cause
16
  * serious issues with the options, so no if ( ! defined() ).}}
17
  */
18
- define( 'WPSEO_VERSION', '14.0.1' );
19
 
20
 
21
  if ( ! defined( 'WPSEO_PATH' ) ) {
@@ -26,11 +26,6 @@ if ( ! defined( 'WPSEO_BASENAME' ) ) {
26
  define( 'WPSEO_BASENAME', plugin_basename( WPSEO_FILE ) );
27
  }
28
 
29
- if ( ! extension_loaded( 'pdo_mysql' ) ) {
30
- require_once WPSEO_PATH . 'polyfills/pdo/pdo-mysqli-polyfill.php';
31
- require_once WPSEO_PATH . 'polyfills/pdo/pdo-mysqli-statement-polyfill.php';
32
- }
33
-
34
  /*
35
  * {@internal The prefix constants are used to build prefixed versions of dependencies.
36
  * These should not be changed on run-time, thus missing the ! defined() check.}}
15
  * {@internal Nobody should be able to overrule the real version number as this can cause
16
  * serious issues with the options, so no if ( ! defined() ).}}
17
  */
18
+ define( 'WPSEO_VERSION', '14.0.2' );
19
 
20
 
21
  if ( ! defined( 'WPSEO_PATH' ) ) {
26
  define( 'WPSEO_BASENAME', plugin_basename( WPSEO_FILE ) );
27
  }
28
 
 
 
 
 
 
29
  /*
30
  * {@internal The prefix constants are used to build prefixed versions of dependencies.
31
  * These should not be changed on run-time, thus missing the ! defined() check.}}
wp-seo.php CHANGED
@@ -8,7 +8,7 @@
8
  *
9
  * @wordpress-plugin
10
  * Plugin Name: Yoast SEO
11
- * Version: 14.0.1
12
  * Plugin URI: https://yoa.st/1uj
13
  * Description: The first true all-in-one SEO solution for WordPress, including on-page content analysis, XML sitemaps and much more.
14
  * Author: Team Yoast
8
  *
9
  * @wordpress-plugin
10
  * Plugin Name: Yoast SEO
11
+ * Version: 14.0.2
12
  * Plugin URI: https://yoa.st/1uj
13
  * Description: The first true all-in-one SEO solution for WordPress, including on-page content analysis, XML sitemaps and much more.
14
  * Author: Team Yoast