Noughts and Crosses winner declaration in a game of variable square board size
About the logic of assessing victory:

After each player's turn there can only be one winner, so no matter how you want to write the functional parts it would be best practice to write a return as soon as a victor is found so that you don't do any surplus processing.

The sample $state data:


You have provided a 4x4 grid, with only one player's marks on it -- which is not realistic.
You use - to identify unmarked cells -- I think blanks would be simpler/clearer.
Player X has a diagonal sequence of 4 marks, even though victory (by your rules) is attained by 3 consecutive marks on a 4x4 grid.


Some missing diagonal checks:

$diUp and $diDn have $count values of 1.  This is accurate on 3x3 grids only.

Because $lengthToWin is sqrt(count($state))-1, all grids larger than 3x3 will have 4 winning diagonal sequences in each orientation (running down-to-the-right &amp; down-to-the-left).

Considering a 4x4 grid, the winning sequences from upper-left to lower-right are:


[ 0 , 5 , 10 ]
[ 1 , 6 , 11 ]
[ 4 , 9 , 14 ]
[ 5 , 10 , 15]


The first and last sequences share the internal cells with indexes of 5 &amp; 10.

Here is a visual:



The same logic is applied to the sequences that start from the upper-right corner.

To crystallize... a 3x3 grid will have a total of 2 diagonal sequences, all other grids will have a total of 8 diagonal sequences.  The diagonal sequences obey a simple pattern, so you will have no trouble in generating the correct path for your function.

My tinkerings:

I experimented a little with what I understood of your concepts and tried to employ array functions rather than for loops (though they are sometimes slower, I think they are better descriptors of action).  It is not a very tidy snippet (consider it a "napkin" draft), I didn't bother refining it because I am not 100% sure of your usage nor what is the best way to display the result.

function hasWon(array $vals,$mark){
    return empty(array_diff($vals,[$mark])); // retain opponent's mark and blanks, if empty = victory / true
}

function findFirstWinningSequence(array $state,$width,$mark){
    $lengthToWin=($width&gt;3?$width-1:$width);
    // process horizontal and vertical sequences:
    $chunks=array_chunk($state,$width);
    foreach($chunks as $i=&gt;$chunk){
        // full-length/width checks:
        if(hasWon(array_slice($chunk,0,$lengthToWin),$mark)){ // scan rows from left
            return "$mark wins via row ".++$i." from left";
        }
        $column=array_column($chunks,$i);  // cache column elements
        if(hasWon(array_slice($column,0,$lengthToWin),$mark)){ // scan cols from top
            return "$mark wins via column ".++$i." from top";
        }
        // length/width-1 checks
        if($width&gt;3 &amp;&amp; hasWon(array_slice($chunk,-$lengthToWin),$mark)){ // scan rows from right, if needed
            return "$mark wins via row ".++$i." from right";
        } 
        if($width&gt;3 &amp;&amp; hasWon(array_slice($column,-$lengthToWin),$mark)){ // scan cols from bottom, if needed
            return "$mark wins via column ".++$i." from bottom";
        } 
    }
    // process UpperLeft-to-LowerRight diagonal sequences:
    $starts=[0];  // index of first element in set
    if($width&gt;3){
        $starts+=[1,$width,$width+1];
    }
    $increment=$width+1;  // the increment between each cell in sequence
    foreach($starts as $start){
        $keys=array_fill_keys(range($start,$start+$increment*($lengthToWin-1),$increment),null);
        // if $start=0 &amp; $width=4, $keys=array_fill_keys(range(0,0+5*(3-1)=10,5),null); producing: [0=&gt;null,5=&gt;null,10=&gt;null]
        if(hasWon(array_intersect_key($state,$keys),$mark)){
            return "$mark wins via diagonal from ".++$start." running down and to the right";
        }
    }
    // process UpperRight-to-LowerLeft diagonal sequences:
    $starts=[$width-1];  // index of first element in set
    if($width&gt;3){
        $starts+=[$width-2,2*($width-1),2*($width-1)+1];
    }
    $increment=$width-1;  // the increment between each cell in sequence
    foreach($starts as $start){
        $keys=array_fill_keys(range($start,$start+$increment*($lengthToWin-1),$increment),null);
        // if $start=3 &amp; $width=5, $keys=array_fill_keys(range(4,3+4*(3)=15,4),null); producing: [3=&gt;null,7=&gt;null,11=&gt;null,15=&gt;null]
        if(hasWon(array_intersect_key($state,$keys),$mark)){
            return "$mark wins via diagonal from ".++$start." running down and to the left";
        }
    }
}
$state=['X','','','','','X','','','','','X','','','','O','O'];
$width=sqrt(sizeof($state));
$mark='X';
echo findFirstWinningSequence($state,$width,$mark);


To conclude, your coding structure seems suitably segmented/clean.  It only requires the logical tunes ups that I mentioned before my snippet.