Skip to content

Instantly share code, notes, and snippets.

@danieliser
Created August 20, 2018 03:28
Show Gist options
  • Save danieliser/c90e952aa4fd2a3dcf991a0cf8eb2b6b to your computer and use it in GitHub Desktop.
Save danieliser/c90e952aa4fd2a3dcf991a0cf8eb2b6b to your computer and use it in GitHub Desktop.
Simple & reusable PHP counter class & methods add, increase, decrease, subtract, and of course ability to run a callback when the value changes for permanat storage. Includes example usage for WordPress Post types and share count, like counts etc.
<?php
/******************************************************************************
* @Copyright (c) 2018, Code Atlantic *
******************************************************************************/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Class Counter
*
* @package ForumWP\Model
*/
class Counter {
/**
* @var int
*/
private $count = 0;
/**
* @var null|callable
*/
private $callback;
/**
* @var null|string
*/
private $context;
/**
* Count constructor.
*
* @param int $count
* @param string $context Used to identify what this counters context is. Mainly for passing to generic callbacks to identify which context to save in.s
* @param null|callable $callback
*/
public function __construct( $count = 0, $context = '', $callback = null ) {
$this->context = $context;
$this->set( $count );
// Callback is set after the count is stored to prevent unwanted calls to the callback.
$this->callback = $callback;
return $this;
}
/**
* @param $count
*/
public function set( $count ) {
$this->count = is_numeric( $count ) ? intval( $count ) : 0;
}
/**
*
*/
public function increase() {
$this->add();
}
/**
* @param int $amount
*/
public function add( $amount = 1 ) {
$this->count = $this->count + intval( $amount );
$this->callback();
}
/**
*
*/
public function decrease() {
$this->subtract();
}
/**
* @param int $amount
*/
public function subtract( $amount = 1 ) {
if ( $this->count == 0 ) {
return;
}
$new_count = $this->count - intval( $amount );
if ( $new_count < 0 ) {
$new_count = 0;
}
$this->count = $new_count;
$this->callback();
}
/**
* @param $name
* @param $value
*/
public function __set( $name, $value ) {
if ( $name != 'count' ) {
$this->$name = $value;
return;
}
$this->count = intval( $value );
$this->callback();
}
/**
*
*/
public function callback() {
if ( is_callable( $this->callback ) ) {
call_user_func( $this->callback, $this->count, $this->context );
}
}
/**
* @return string
*/
public function __toString() {
return (string) $this->count;
}
/**
* @param null $key
*
* @return bool
*/
public function __isset( $key = null ) {
return (bool) is_numeric( $this->count );
}
/**
* @return array
*/
public function __sleep() {
return array( 'count', 'context', 'callback' );
}
}
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Class Post
*/
class Post {
public $ID;
/**
* Holds various counters by $context.
*
* @var Counter[]
*/
public $counters = array();
public function __construct( $id ) {
$this->ID = $id;
}
/**
* Gets the current count for a given $context.
*
* @param string $context
* @param bool $force
*
* @return int
*/
public function get_count( $context, $force = false ) {
return (int) (string) $this->counter( $context, $force );
}
/**
* Gets a Counter instance for given context
*
* @param string $context
* @param bool $force
*
* @return Counter|\WP_Error
*/
public function counter( $context, $force = false ) {
$meta_key = $context . '_count';
if ( ! isset( $this->counters[ $context ] ) || $force ) {
$count = $this->get_meta( $meta_key ) ? intval( $this->get_meta( $meta_key ) ) : 0;
$this->counters[ $context ] = new Counter( $count, $context, array( $this, "update_count" ) );
}
return $this->counters[ $context ];
}
/**
* Updates count meta.
*
* @param int|string $count
* @param string $which
*/
public function update_count( $count, $which ) {
$this->update_meta( $which . '_count', $count );
}
/**
* @param $key
* @param bool $single
*
* @return mixed|false
*/
public function get_meta( $key, $single = true ) {
return get_post_meta( $this->ID, $key, $single );
}
/**
* @param string $key
* @param mixed $value
*
* @return bool|int
*/
public function update_meta( $key, $value ) {
return update_post_meta( $this->ID, $key, $value );
}
/**
* serialize() checks if your class has a function with the magic name __sleep.
* If so, that function is executed prior to any serialization.
* It can clean up the object and is supposed to return an array with the names of all variables of that object that should be serialized.
* If the method doesn't return anything then NULL is serialized and E_NOTICE is issued.
* The intended use of __sleep is to commit pending data or perform similar cleanup tasks.
* Also, the function is useful if you have very large objects which do not need to be saved completely.
*
* @return string[]
* @link http://php.net/manual/en/language.oop5.magic.php#language.oop5.magic.sleep
*/
public function __sleep() {
foreach ( $this->counters as $context => $counter ) {
$this->counters[ $context ] = (int) (string) $counter;
}
return array_keys( get_object_vars( $this ) );
}
/**
* unserialize() checks for the presence of a function with the magic name __wakeup.
* If present, this function can reconstruct any resources that the object may have.
* The intended use of __wakeup is to reestablish any database connections that may have been lost during
* serialization and perform other reinitialization tasks.
*
* @return void
* @link http://php.net/manual/en/language.oop5.magic.php#language.oop5.magic.sleep
*/
public function __wakeup() {
// Reinitialize counters.s
foreach ( $this->counters as $context => $count ) {
$this->counters[ $context ] = new Counter( $count, $context, array( $this, "update_count" ) );
}
}
}
$post = new Post( 1 );
$share_count = $post->get_count( 'share' ); // Retrieves a new Counter class with the value of get_post_meta( 1, 'share_count', true );
$post->counter( 'share' )->increase(); // Add 1 to the count, which will subsequently update post meta.
$post->counter( 'share' )->decrease(); // Subtract 1 from the counter & save.
$post->counter( 'share' )->add( 10 ); // Add 10 to the counter & save.
$post->counter( 'share' )->subtract( 10 ); // Subtract 10 from the counter & save.
// Manage multiple counters quickly & easily, each with its own meta key storage.
$like_count = $post->get_count( 'like' );
$conversions = $post->get_count( 'conversion' );
// It even supports serialization.
$serialized = serialize( $post ); // a:1:{s:8:"counters";a:3:{s:5:"share";i:0;s:4:"like";i:10;s:10:"conversion";i:3;}}
// You could also add quick access functions that wrap counters, such as function get_like_count().
// And you can access counts directly with a little __get() magic, returning them as (int) (string) will do nicely.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment