Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #217: added ClientFactory to make sure PHP instances without cURL and/or allow_url_fopen turned on will fail softly. #218

Merged
merged 7 commits into from
Jul 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 13 additions & 27 deletions src/Admin/Provisioning.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,17 @@
use Plausible\Analytics\WP\Client\Model\GoalCreateRequestCustomEvent;
use Plausible\Analytics\WP\Client\Model\GoalCreateRequestPageview;
use Plausible\Analytics\WP\Client\Model\GoalCreateRequestRevenue;
use Plausible\Analytics\WP\ClientFactory;
use Plausible\Analytics\WP\Helpers;
use Plausible\Analytics\WP\Integrations;
use Plausible\Analytics\WP\Integrations\WooCommerce;

class Provisioning {
/**
* @var ClientFactory
*/
private $client_factory;

/**
* @var Client $client
*/
Expand Down Expand Up @@ -54,19 +60,11 @@ class Provisioning {
* @codeCoverageIgnore
*/
public function __construct( $client = null ) {
/**
* cURL or allow_url_fopen ini setting is required for GuzzleHttp to function properly.
*/
if ( ! extension_loaded( 'curl' ) && ! ini_get( 'allow_url_fopen' ) ) {
add_action( 'init', [ $this, 'add_curl_error' ] );

return;
}

$this->client = $client;

if ( ! $this->client ) {
$this->client = new Client();
$this->client_factory = new ClientFactory();
$this->client = $this->client_factory->build();
}

$this->custom_event_goals = [
Expand All @@ -88,7 +86,7 @@ public function __construct( $client = null ) {
* @codeCoverageIgnore
*/
private function init() {
if ( ! $this->client->validate_api_token() ) {
if ( ! $this->client instanceof Client || ! $this->client->validate_api_token() ) {
return; // @codeCoverageIgnore
}

Expand All @@ -100,22 +98,6 @@ private function init() {
add_action( 'update_option_plausible_analytics_settings', [ $this, 'maybe_create_custom_properties' ], 11, 2 );
}

/**
* Show an error on the settings screen if cURL isn't enabled on this machine.
*
* @return void
*
* @codeCoverageIgnore
*/
public function add_curl_error() {
Messages::set_error(
__(
'cURL is not enabled on this server, which means API provisioning will not work. Please contact your hosting provider to enable the cURL module or <code>allow_url_fopen</code>.',
'plausible-analytics'
)
);
}

/**
* Create shared link when Enable Analytics Dashboard option is enabled.
*
Expand Down Expand Up @@ -347,6 +329,8 @@ public function maybe_delete_goals( $old_settings, $settings ) {
* @param $settings
*
* @return void
*
* @codeCoverageIgnore Because we don't want to test if the API is working.
*/
public function maybe_delete_woocommerce_goals( $old_settings, $settings ) {
$enhanced_measurements = array_filter( $settings[ 'enhanced_measurements' ] );
Expand Down Expand Up @@ -381,6 +365,8 @@ public function maybe_delete_woocommerce_goals( $old_settings, $settings ) {
* @param array $haystack
*
* @return false|mixed
*
* @codeCoverageIgnore Because it can't be unit tested.
*/
private function array_search_contains( $string, $haystack ) {
if ( preg_match( '/\([A-Z]*?\)/', $string ) ) {
Expand Down
12 changes: 10 additions & 2 deletions src/Admin/Settings/Page.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

use Exception;
use Plausible\Analytics\WP\Client;
use Plausible\Analytics\WP\ClientFactory;
use Plausible\Analytics\WP\Helpers;

class Page extends API {
Expand Down Expand Up @@ -59,6 +60,11 @@ class Page extends API {
*/
public $fields = [];

/**
* @var ClientFactory $client_factory
*/
private $client_factory;

/**
* @var Client $client
*/
Expand All @@ -77,8 +83,9 @@ public function __construct() {

$settings = Helpers::get_settings();

$this->client = new Client();
$this->fields = [
$this->client_factory = new ClientFactory();
$this->client = $this->client_factory->build();
$this->fields = [
'general' => [
[
'label' => esc_html__( 'Connect your website with Plausible Analytics', 'plausible-analytics' ),
Expand Down Expand Up @@ -118,6 +125,7 @@ public function __construct() {
'type' => 'button',
'disabled' => empty( $settings[ 'domain_name' ] ) ||
empty( $settings[ 'api_token' ] ) ||
! $this->client instanceof Client ||
$this->client->is_api_token_valid(),
],
],
Expand Down
5 changes: 3 additions & 2 deletions src/Ajax.php
Original file line number Diff line number Diff line change
Expand Up @@ -313,9 +313,10 @@ public function save_options() {
* @throws ApiException
*/
private function validate_api_token( $token = '' ) {
$client = new Client( $token );
$client_factory = new ClientFactory( $token );
$client = $client_factory->build();

if ( ! $client->validate_api_token() ) {
if ( $client instanceof Client && ! $client->validate_api_token() ) {
$hosted_domain = Helpers::get_hosted_domain_url();
$domain = Helpers::get_domain();

Expand Down
55 changes: 55 additions & 0 deletions src/ClientFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

namespace Plausible\Analytics\WP;

use Plausible\Analytics\WP\Admin\Messages;

class ClientFactory {
/**
* @var string $token
*/
private $token;

/**
* Setup basic authorization.
*
* @param string $token Allows to specify the token, e.g. when it's not stored in the DB yet.
*/
public function __construct( $token = '' ) {
$this->token = $token;
}

/**
* Loads the Client class if all conditions are met.
*
* @return false|Client
*/
public function build() {
/**
* cURL or allow_url_fopen ini setting is required for GuzzleHttp to function properly.
*/
if ( ! extension_loaded( 'curl' ) && ! ini_get( 'allow_url_fopen' ) ) {
add_action( 'init', [ $this, 'add_curl_error' ] ); // @codeCoverageIgnore

return false; // @codeCoverageIgnore
}

return new Client( $this->token );
}

/**
* Show an error on the settings screen if cURL isn't enabled on this machine.
*
* @return void
*
* @codeCoverageIgnore
*/
public function add_curl_error() {
Messages::set_error(
__(
'cURL is not enabled on this server, which means API provisioning will not work. Please contact your hosting provider to enable the cURL module or <code>allow_url_fopen</code>.',
'plausible-analytics'
)
);
}
}
26 changes: 3 additions & 23 deletions src/Filters.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ class Filters {
*/
public function __construct() {
add_filter( 'script_loader_tag', [ $this, 'add_plausible_attributes' ], 10, 2 );
add_filter( 'rest_url', [ $this, 'wpml_compatibility' ], 10, 1 );
add_filter( 'plausible_analytics_script_params', [ $this, 'maybe_add_custom_params' ] );
}

Expand All @@ -43,7 +42,7 @@ public function __construct() {
* @return string
*/
public function add_plausible_attributes( $tag, $handle ) {
// Bailout, if not `Plausible Analytics` script.
// Bail if it's not our script.
if ( 'plausible-analytics' !== $handle ) {
return $tag; // @codeCoverageIgnore
}
Expand All @@ -61,34 +60,15 @@ public function add_plausible_attributes( $tag, $handle ) {

// Triggered when exclude pages is enabled.
if ( ! empty( $settings[ 'excluded_pages' ] ) && $settings[ 'excluded_pages' ] ) {
$excluded_pages = $settings[ 'excluded_pages' ];
$params .= " data-exclude='{$excluded_pages}'";
$excluded_pages = $settings[ 'excluded_pages' ]; // @codeCoverageIgnore
$params .= " data-exclude='{$excluded_pages}'"; // @codeCoverageIgnore
}

$params = apply_filters( 'plausible_analytics_script_params', $params );

return str_replace( ' src', " {$params} src", $tag );
}

/**
* WPML overrides the REST API URL to include the language 'subdirectory', which leads to 404s.
* This forces it back to default behavior.
*
* @param mixed $url
*
* @return string|void
* @throws Exception
*/
public function wpml_compatibility( $url ) {
$rest_endpoint = Helpers::get_rest_endpoint( false );

if ( strpos( $url, $rest_endpoint ) !== false ) {
return get_option( 'home' ) . $rest_endpoint; // @codeCoverageIgnore
}

return $url;
}

/**
* Adds custom parameters Author and Category if Custom Pageview Properties is enabled.
*
Expand Down
4 changes: 2 additions & 2 deletions src/Integrations/WooCommerce.php
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ public function track_remove_cart_item( $cart_item_key, $cart ) {
*/
public function track_entered_checkout() {
if ( ! is_checkout() ) {
return;
return; // @codeCoverageIgnore
}

$cart = WC()->cart;
Expand Down Expand Up @@ -298,7 +298,7 @@ public function track_purchase( $order_id ) {
$is_tracked = $order->get_meta( self::PURCHASE_TRACKED_META_KEY );

if ( $is_tracked ) {
return;
return; // @codeCoverageIgnore
}

$props = wp_json_encode(
Expand Down
22 changes: 22 additions & 0 deletions tests/unit/ClientFactoryTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php
/**
* @package Plausible Analytics Unit Tests - ClientFactory
*/

namespace Plausible\Analytics\Tests\Unit;

use Plausible\Analytics\Tests\TestCase;
use Plausible\Analytics\WP\Client;
use Plausible\Analytics\WP\ClientFactory;

class ClientFactoryTest extends TestCase {
/**
* @see ClientFactory::build()
*/
public function testBuild() {
$clientFactory = new ClientFactory();
$client = $clientFactory->build();

$this->assertInstanceOf( Client::class, $client );
}
}