<commit_before>module.exports = function substr (str, start, len) {
  //  discuss at: https://locutus.io/php/substr/
  // original by: <NAME>
  // bugfixed by: T.Wild
  // improved by: <NAME> (https://twitter.com/onnomarsman)
  // improved by: <NAME> (https://brett-zamir.me)
  //  revised by: Theriault (https://github.com/Theriault)
  //      note 1: Handles rare Unicode characters if 'unicode.semantics' ini (PHP6) is set to 'on'
  //   example 1: substr('abcdef', 0, -1)
  //   returns 1: 'abcde'
  //   example 2: substr(2, 0, -6)
  //   returns 2: false
  //   example 3: ini_set('unicode.semantics', 'on')
  //   example 3: substr('a\uD801\uDC00', 0, -1)
  //   returns 3: 'a'
  //   example 4: ini_set('unicode.semantics', 'on')
  //   example 4: substr('a\uD801\uDC00', 0, 2)
  //   returns 4: 'a\uD801\uDC00'
  //   example 5: ini_set('unicode.semantics', 'on')
  //   example 5: substr('a\uD801\uDC00', -1, 1)
  //   returns 5: '\uD801\uDC00'
  //   example 6: ini_set('unicode.semantics', 'on')
  //   example 6: substr('a\uD801\uDC00z\uD801\uDC00', -3, 2)
  //   returns 6: '\uD801\uDC00z'
  //   example 7: ini_set('unicode.semantics', 'on')
  //   example 7: substr('a\uD801\uDC00z\uD801\uDC00', -3, -1)
  //   returns 7: '\uD801\uDC00z'
  //        test: skip-3 skip-4 skip-5 skip-6 skip-7

  var _php_cast_string = require('../_helpers/_phpCastString') // eslint-disable-line camelcase

  str = _php_cast_string(str)

  var end = str.length
  var ini_get = require('../info/ini_get') // eslint-disable-line camelcase
  var surrogatePair = /[\uD800-\uDBFF][\uDC00-\uDFFF]/
  var multibyte = ini_get('unicode.semantics') === 'on' && surrogatePair.test(str)

  if (!multibyte) {
    // assumes there are no non-BMP characters;
    // if there may be such characters, then it is best to turn it on (critical in true XHTML/XML)
    if (start < 0) {
      start += end
    }
    if (typeof len !== 'undefined') {
      if (len < 0) {
        end = len + end
      } else {
        end = len + start
      }
    }

    // PHP returns false if start does not fall within the string.
    // PHP returns false if the calculated end comes before the calculated start.
    // PHP returns an empty string if start and end are the same.
    // Otherwise, PHP returns the portion of the string from start to end.
    if (start >= str.length || start < 0 || start > end) {
      return false
    }
<commit_msg>Fix issue 408: substr returns unexpected false when input string is empty<commit_after>module.exports = function substr (str, start, len) {
  //  discuss at: https://locutus.io/php/substr/
  // original by: <NAME>
  // bugfixed by: T.Wild
  // improved by: <NAME> (https://twitter.com/onnomarsman)
  // improved by: <NAME> (https://brett-zamir.me)
  //  revised by: Theriault (https://github.com/Theriault)
  //      note 1: Handles rare Unicode characters if 'unicode.semantics' ini (PHP6) is set to 'on'
  //   example 1: substr('abcdef', 0, -1)
  //   returns 1: 'abcde'
  //   example 2: substr(2, 0, -6)
  //   returns 2: false
  //   example 3: ini_set('unicode.semantics', 'on')
  //   example 3: substr('a\uD801\uDC00', 0, -1)
  //   returns 3: 'a'
  //   example 4: ini_set('unicode.semantics', 'on')
  //   example 4: substr('a\uD801\uDC00', 0, 2)
  //   returns 4: 'a\uD801\uDC00'
  //   example 5: ini_set('unicode.semantics', 'on')
  //   example 5: substr('a\uD801\uDC00', -1, 1)
  //   returns 5: '\uD801\uDC00'
  //   example 6: ini_set('unicode.semantics', 'on')
  //   example 6: substr('a\uD801\uDC00z\uD801\uDC00', -3, 2)
  //   returns 6: '\uD801\uDC00z'
  //   example 7: ini_set('unicode.semantics', 'on')
  //   example 7: substr('a\uD801\uDC00z\uD801\uDC00', -3, -1)
  //   returns 7: '\uD801\uDC00z'
  //        test: skip-3 skip-4 skip-5 skip-6 skip-7

  var _php_cast_string = require('../_helpers/_phpCastString') // eslint-disable-line camelcase

  str = _php_cast_string(str)

  var end = str.length
  var ini_get = require('../info/ini_get') // eslint-disable-line camelcase
  var surrogatePair = /[\uD800-\uDBFF][\uDC00-\uDFFF]/
  var multibyte = ini_get('unicode.semantics') === 'on' && surrogatePair.test(str)

  if (!multibyte) {
    // assumes there are no non-BMP characters;
    // if there may be such characters, then it is best to turn it on (critical in true XHTML/XML)
    if (start < 0) {
      start += end
    }
    if (typeof len !== 'undefined') {
      if (len < 0) {
        end = len + end
      } else {
        end = len + start
      }
    }

    // PHP returns false if start does not fall within the string.
    // PHP returns false if the calculated end comes before the calculated start.
    // PHP returns an empty string if start and end are the same.
    // Otherwise, PHP returns the portion of the string from start to end.
    if (start > str.length || start < 0 || start > end) {
      return false
    }

{'fraction_non_alphanumeric': 0.15809952897808724, 'fraction_numerical': 0.04321114069219742, 'mean_word_length': 2.4835948644793153, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 13, 'https://': 8, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4594101', 'n_tokens_mistral': 1964, 'n_tokens_neox': 1754, 'n_words': 505}