Для получения полного доступа
зарегистрируйтесь.

Section:
Sub-section::
Languages:


IStranger
  • Reputation: 17
  • Snippets: 8
  • Revisions: 0

Often we need remove CSS classes that begin with certain string. For example: has-state-active, has-state-pending, has-state-disabled. Easiest way is to use wildcards: has-state-*.

Following function supports them (requires jQuery):

/**
 * Removes CSS classes of element using wildcards:
 *
 * @example
 * <code><pre>
 *
 *          // create some element
 *      var $element = $('<div id="#some-element"/>')
 *                          .addClass('base-class has-state-active item item-75')
 *                          .appendTo('body');
 *
 *          // remove classes with few prefixes
 *      removeClassWildcard( $element, 'has-state-* item-*');
 *
 *          // display element classes
 *      console.log($element.attr('class'));    // >> "base-class item"
 *
 * </pre></code>
 *
 * @param {jQuery|*}    $element
 * @param {String}      removals        Classes to delete, for example: 'foo-* bar-*'
 * @returns {jQuery|*}                  Passed element
 */
function removeClassWildcard($element, removals) {
    if (removals.indexOf('*') === -1) {
        // Use native jQuery methods if there is no wildcard matching
        $element.removeClass(removals);
        return $element;
    }

    var patt = new RegExp('\\s' +
            removals.replace(/\*/g, '[A-Za-z0-9-_]+').split(' ').join('\\s|\\s') +
            '\\s', 'g');

    $element.each(function (i, it) {
        var cn = ' ' + it.className + ' ';
        while (patt.test(cn)) {
            cn = cn.replace(patt, ' ');
        }
        it.className = $.trim(cn);
    });

    return $element;
}

Example:


  // create some element
var $element = $('<div id="#some-element"/>')
                  .addClass('base-class has-state-active item item-75')
                  .appendTo('body');

  // remove classes with few prefixes
removeClassWildcard( $element, 'has-state-* item-*');

  // display element classes
console.log($element.attr('class'));    // >> "base-class item"
IStranger
  • Reputation: 17
  • Snippets: 8
  • Revisions: 0

"Golden" rule of frontend optimization often recommends to insert all JS scripts on page end. However usualy we can't remove <script src="//jquery.js"> from page <head>, because page content can contain many onReady-handlers, for example $(function(){ ... }).

This short snippet allows move jquery.js from <head> to end of <body> without changing onReady-handlers.

Snippet:

<head>
	<script>
		// Fallback code for the jQuery inline scripts on pages:
      if (window.jQuery === undefined) {
          window.jQueryReadyHandlers = [];

          window.$ = window.jQuery = function (callback) {
              window.jQueryReadyHandlers.push(callback);
              return window.$;
          };

          window.$.ready = window.$;
      }
  	</script>
    <!-- ...some head items -->
</head>
<body>

	<!-- ...some page content -->
    
    <script> 
    	// some onReady-handlers
      $(function(){
          console.log('First callback');
      });

      jQuery(document).ready(function(){
          console.log('Second callback');
      });
    </script>
    
    <!-- ...some page content -->


	<script src="//js/jquery.min.js"></script>
    <script>
    	jQuery(window).load(function(){
        	if(window.jQueryReadyHandlers) { 
            	$.each(window.jQueryReadyHandlers, function(index,func){  
                	$(func);
                });
            }
        });
    </script>
</body>
IStranger
  • Reputation: 17
  • Snippets: 8
  • Revisions: 0

When we work with some API interface, it often have Rate-Limit, usually certain number of http-requests per unit of time (see for example Twitter API Rate Limit).

This helper can help you efficiently handle these restrictions.

Helper code:


/**
 * Helper for work with some API.
 */
class ApiHelper
{

    /**
     * Wraps specified callback function and retries of execution in the case when callback throws specified exception.
     * Used Exponential BackOff algorithm for evaluating of timeouts between retries of callback execution.
     *
     * Example:
     * <code>
     *  $sendHttpRequest = function ($pageNum) use ($httpClient){
     *      $httpClient->sendGet('http://example.com?pageNum=' . $pageNum);
     *  };
     *
     *  $wrappedFn = ApiHelper::wrapUsingExponentialBackOff($sendHttpRequest, 5000, '\TooManyRequestsHttpException');
     *
     *  // We perform 100 requests. But remote server has RateLimit.
     *  for ($i = 1; $i <= 100; $i++) {
     *      // $sendHttpRequest($i);     // request without wrapper
     *      $wrappedFn($i);              // request with wrapper
     *      // this wrapper retry callback execution in the case of \TooManyRequestsHttpException exception.
     *  }
     * </code>
     *
     * @param \Closure $callbackFn                  Callback function.
     * @param int      $maxSleepMs                  Max value of timeout between callback execution.
     * @param string   $rateLimitExceptionClassName Exception that should throw callback on rate limit.
     * @param int      $maxRetries                  Max number of retries.
     * @param \Closure $onTooManyRequest            Callback function that performed on each RateLimit exception.
     *
     * @return \Closure
     */
    public static function wrapUsingExponentialBackOff(\Closure $callbackFn, $maxSleepMs, $rateLimitExceptionClassName, $maxRetries = 10, \Closure $onTooManyRequest = null)
    {
        $wrapFn = function () use ($callbackFn, $maxSleepMs, $rateLimitExceptionClassName, $maxRetries, $onTooManyRequest) {

            $currentTry    = 0;
            $retriesRemain = $maxRetries;

            while ($retriesRemain-- > 0) {

                try {
                    $currentTry++;
                    return call_user_func_array($callbackFn, func_get_args());

                } catch (\Exception $exception) {

                    // retries only throttling errors
                    if ($exception instanceof $rateLimitExceptionClassName) {
                        $backOffDuration = static::_getSleepDuration($currentTry, 10, $maxSleepMs);

                        if ($onTooManyRequest) {
                            $onTooManyRequest($exception, $backOffDuration, $retriesRemain, $currentTry);
                        }

                        usleep($backOffDuration * 1000);
                    } else {
                        throw $exception;
                    }

                }

            }

            throw new \Exception('ExponentialBackOff: Too many retries of function call [maxRetries=' . $maxRetries . ']');
        };

        return $wrapFn;
    }

    protected static function _getSleepDuration($currentTry, $minSleepMs, $maxSleepMs)
    {
        $currentSleepMs = (int)($minSleepMs * pow(2, $currentTry));

        return min($currentSleepMs, $maxSleepMs);
    }
}

How it use

  1. Prepare your callback function that performs some API method. This callback function should throw certain exception when API not allows perform method (restricts by Rate-Limit).
  2. Wrap your callback function using ApiHelper::wrapUsingExponentialBackOff() method
  3. Execute wrapped callback function (instead original function).
$sendHttpRequest = function ($pageNum) use ($httpClient) {
    $httpClient->sendGet('http://example.com?pageNum=' . $pageNum);
};

$wrappedFn = ApiHelper::wrapUsingExponentialBackOff($sendHttpRequest, 5000, '\TooManyRequestsHttpException');

// We perform 100 requests. But remote server has RateLimit.
for ($i = 1; $i <= 100; $i++) {
    // $sendHttpRequest($i);     // request without wrapper
    $wrappedFn($i);              // request with wrapper
    // this wrapper retry callback execution in the case of \TooManyRequestsHttpException exception.
}

How it works

This helper uses "Exponential backoff" algorithm. The main idea of this algorithm is to reduce the rate at which you are executing an operation by introducing delays. Therefore when you are "backing off" you are waiting for a period of time before attempting to execute the operation again. Wrapped callback function $wrappedFn will retry to execute specified original callback function $sendHttpRequest if it throws exception.

Snippet,  PHP

mb_ucfirst

samdark
  • Reputation: 348
  • Snippets: 57
  • Revisions: 5

Mulibyte versions of ucfirst() which works well with unicode.

function mb_ucfirst($string, $encoding = 'UTF-8') {
  $firstChar = mb_strtoupper(mb_substr($string, 0, 1, $encoding), $encoding);
  return $firstChar . mb_substr($string, 1, mb_strlen($string, $encoding), $encoding);
}
dignityinside
  • Reputation: 26
  • Snippets: 21
  • Revisions: 1

Function to sort an array according to its keys which are floats casted as strings.

function sort_string_float($a, $b) {

	$a = (float) $a;
	$b = (float) $b;

	if ($a == $b) {
		return 0;
	}

	return $a < $b ? -1 : 1;

}

// use
uksort($l1_data, 'self::sort_string_float');
dignityinside
  • Reputation: 26
  • Snippets: 21
  • Revisions: 1

Small bash script to delete redis cache.

 redis-cli KEYS "username*" | while read key; do redis-cli DEL $key; echo $key; done
dignityinside
  • Reputation: 26
  • Snippets: 21
  • Revisions: 1

Check whether value an array is not empty.

$arr = ['', 'apple', ''];

if (!empty(array_filter($arr))) {
    // do something
}
samdark
  • Reputation: 348
  • Snippets: 57
  • Revisions: 5

It's totally irritating when you have Mapbox or Leaflet map on a long long page. When you're scrolling map comes into the view it is starting to zoom map out instead of scrolling the page.

It could be fixed by requiring three second hover over the map before it could be scrolled.

var map = L.mapbox.map('mymap', 'mymap');

// disable scroll-zooming initially
map.scrollWheelZoom.disable();

var zoomTimer;

// on entering the map we're starting a timer to
// 3 seconds after which we'll enable scroll-zooming
map.on('mouseover', function() {
	zoomTimer = setTimeout(function(){
		map.scrollWheelZoom.enable();
    }, 3000);
});

// on leaving the map we're disarming not yet triggered timers
// and disabling scrolling
map.on('mouseout', function() {
    clearTimeout(zoomTimer);
    map.scrollWheelZoom.disable();
});

samdark
  • Reputation: 348
  • Snippets: 57
  • Revisions: 5

After posting about fast hydration I've got response with even faster method that doesn't require extra class. Credits go to Vladimir Chub.

$postHydrator = function(array $data) {
    $this->id = $data['id'];
    $this->title = $data['title'];
    $this->text = $data['text'];
};

$postExtractor = function() {
    return [
        'id' => $this->id,
        'title' => $this->title,
        'text' => $this->text
    ];
}

// $data is from database

$post = new Post();
$hydrator = $postHydrator->bindTo($post, $post);
$hydrator($data);

samdark
  • Reputation: 348
  • Snippets: 57
  • Revisions: 5

It's important for object oriented programming and domain driven design not to violate incapsulation i.e. what should not be set by developer should be private or protected:

class Post
{
    protected $id;
    protected $title;
    protected $text;

    public function getId()
    {
        return $this->id;
    }

    public function getTitle()
    {
        return $this->title;
    }

    public function setTitle($title)
    {
        $this->title = $title;
    }

    public function getText()
    {
        return $this->text;
    }

    public function setText($text)
    {
        $this->text = $text;
    }
}

When it comes to pesisting data in database and getting it back there's a problem of filling properties we can't set. Usually it's solved by using reflection but it's quite slow when it comes to hundreds of objects. Marco Pivetta posted a better approach which despite being meant for Doctrine internals is quite simple and interesting.

class PostSelfHydratingProxy extends Post
{
    /** zero-argument constructor since this is just a helper class for hydration */
    public function __construct()
    {
    }
    
    public function hydrate($data, Post $object)
    {
		$object->id = $data['id'];
        $object->title = $data['title'];
        $object->text = $data['text'];
    }

    
    public function extract(Post $object)
    {
        return [
            'id' => $object->id,
            'title' => $object->title,
            'text' => $object->text
        ];
    }
}

// $data is from database

$post = new Post();
$proxy = new PostSelfHydratingProxy();
$proxy->hydrate($data, $post);