Simple Gender Guesser I have made a simple gender guesser for an app and thought that this simple bit of code may be useful to other developers. It takes in a string and returns an 'f' for Female and an 'f' Male depending on the name provided. I am not a PHP expert so I was wondering if anyone can give me some feedback on this code.

The full code can be found at the repository here

&lt;?php

Class Gender{
    public $gender;
    protected $maleNames    = [];
    protected $femaleNames  = [];
    public $isMale      = FALSE;
    public $isFemale    = FALSE;
    public function __construct($name, $default, $unknown, $custom){
        $this-&gt;maleNames    = $this-&gt;getMaleNames($custom);
        $this-&gt;femaleNames  = $this-&gt;getFemaleNames($custom);
        $this-&gt;gender       = $default;
        if(in_array($this-&gt;getName($name), $this-&gt;maleNames)){
            $this-&gt;isMale = TRUE;
        }
        if(in_array($this-&gt;getName($name), $this-&gt;femaleNames)){
            $this-&gt;isFemale = TRUE;
        }
        if($this-&gt;isMale &amp;&amp; !$this-&gt;isFemale){
            $this-&gt;gender = 'm';
        }elseif(!$this-&gt;isMale &amp;&amp; $this-&gt;isFemale){
            $this-&gt;gender = 'f';
        }elseif($this-&gt;isMale &amp;&amp; $this-&gt;isFemale){  
            $this-&gt;gender = $default;
        }else{
            $this-&gt;gender = $unknown;
        }
    }
    public static function guess($name, $default = 'm', $unknown = 'm', $custom = []){
        $g = new Self($name, $default, $unknown, $custom);
        return $g-&gt;gender;
    }
    public static function isMale($name, $default = 'm', $unknown = 'm', $custom = []){
        $g = new Self($name, $default, $unknown, $custom);
        return ($g-&gt;gender === 'm')? TRUE : FALSE;
    }
    public static function isFemale($name, $default = 'm', $unknown = 'm', $custom = []){
        $g = new Self($name, $default, $unknown, $custom);
        return ($g-&gt;gender === 'f')? TRUE : FALSE;
    }
    protected function getName($name){
        $name = explode(' ', $name);
        $name = $name[0];
        $name = strtolower($name);
        return $name;
    }
    protected function getMaleNames($custom){
        $names = json_decode(file_get_contents(__DIR__ . '/names/male.json'));

        if(is_array($custom['male'])){
            foreach($custom['male'] as $name){
                $names[] = strtolower($name);
            }
        }
        return $names;
    }
    protected function getFemaleNames($custom){
        $names = json_decode(file_get_contents(__DIR__ . '/names/female.json'));

        if(is_array($custom['female'])){
            foreach($custom['female'] as $name){
                $names[] = strtolower($name);
            }
        }
        return $names;
    }
}

echo Gender::guess('anthony');

?&gt;
Performance aspects regarding file access

In your getMaleNames($custom) function you first load the /names/male.json contents, then look at $custom and use this one if not empty: so in the latter case you spent time reading file while it's useless. You'd instead look at $custom first, then at file if needed.
Obviously the same applies to getFemaleNames().

In the other hand, when you call one of your static functions you create a new Gender instance, so invoking both of the get...Names() functions, which ends with reading two files for each external call as soon as they don't provide non empty $custom argument. 
It could be far more efficient to create a unique instance once, then use its contents.
As a first thought this might be achieved by looking at the Gender instance from the static functions, and create it only when it doesn't exist yet. But look also at the strategy part below.

Compacting code

There are several places where this can be done.

The best example is in the already cited get...Names() functions: they can be simply factorized like this (also includes the change suggested about files):

protected function getNames($gender, $custom) {
  if (is_array($custom[$gender])) {
    foreach ($custom[$gender] as $name) {
      $names[] = strtolower($name);
    }
  }
  $names =
    json_decode(file_get_contents(__DIR__ . '/names/' . $gender . '.json'));
  return $names;
}


Another example is that, even if you want to keep separate functions available for the external calls (though guess() only is probably enough), you can simplify the two is...() functions, like this one (same for isFemale()):

public static function isMale($name, $default = 'm', $unknown = 'm', $custom = []) {
  return self::guess($name, $default, $unknown, $custom) === 'm';
}


Finally the getName() function can be simplified like this:

protected function getName($name) {
  return strtolower(explode(' ', $name)[0]);
}


Strategy

Analyzing what your class is made for, it appears that creating an instance is not required. The only data which needs to be saved between two external calls is the content of the /names/...json files (and only if we retain the first change I proposed above!).

So you might:


keep properties $maleNames and $femaleNames only, and make them static.
also make the protected functions static.
move all the logics currently in __construct() to guess() (working with simple variables rather than properties).
and not have __construct() at all.


Additionally observe that, in the current __construct() logics:


the affectation to $this-&gt;gender is never used further
getName() can be called only once and is very simple, so the function can be abandoned in profit of direct comptuting
the if/else sequence can be replaced by directs returns


So it ends with this full code simplified version:

Class Gender{
  protected static $maleNames;
  protected static $femaleNames;
  public static function guess($name, $default = 'm', $unknown = 'm', $custom = []){
    if (!self::$maleNames) {
      self::$maleNames = self::getNames('male');
    }
    if (!self::$femaleNames) {
      self::$femaleNames = self::getNames('female');
    }
    $name = strtolower(explode(' ', $name)[0];
    $isMale = in_array($name, self::$maleNames);
    $isFemale = in_array($name, self::$femaleNames);
    if ($isMale &amp;&amp; !isFemale) {
      return 'm';
    }
    if (!$isMale &amp;&amp; isFemale) {
      return 'f';
    }
    if ($isMale &amp;&amp; isFemale) {
      return $default;
    }
    return $unknown;
  }
  public static function isMale($name, $default = 'm', $unknown = 'm', $custom = []) {
    return self::guess($name, $default, $unknown, $custom) === 'm';
  }
  public static function isFemale($name, $default = 'm', $unknown = 'm', $custom = []) {
    return self::guess($name, $default, $unknown, $custom) === 'f';
  }
  protected static function getNames($gender, $custom) {
    if (is_array($custom[$gender])) {
      foreach ($custom[$gender] as $name) {
        $names[] = strtolower($name);
      }
    }
    $names =
      json_decode(file_get_contents(__DIR__ . '/names/' . $gender . '.json'));
    return $names;
  }
}