A function that doesn't use begin, process and end blocks implicitly behaves as if its body were inside an end block; that is, the body isn't executed until after all pipeline input has been received.
In an (implied) end block, you can use @(), the array-subexpression operator to collect all pipeline input ($input) in an [object[]] array:
function foo {
$allPipelineInputAsArray = @($input) # creates [object[]] array
$allPipelineInputAsArray.ForEach({ "[$_]" }) # sample output
}
The above relies on $input implementing the IEnumerator interface, which means that you can also use it directly in a foreach loop, for instance:
function foo {
foreach ($obj in $input) {
"[$obj]" # sample output.
}
}
Both function definitions, when invoked as 1..3 | foo, yield the following:
[1]
[2]
[3]
Caveat: If you define your function as an advanced (cmdlet-like) function (explicitly, via a [CmdletBinding(...)] attribute or implicitly, via a [Parameter(...)] attribute, the automatic $input variable is no longer available - you would then have to manually collect all input objects in a list data structure via the process block, which you can then process in the end block - see the second function definition in Mathias R. Jessen's answer.
Also note that by using this approach you're forgoing the core feature of the PowerShell pipeline, namely the memory-throttling, one-by-one processing of input objects (you're collecting all input objects in memory); however, this would likely only become a problem with very large input sets.
As for what you tried:
[Object[]] $input
Since $input doesn't implement IEnumerable - only its auxiliary IEnumerator interface - this cast doesn't cause the enumerator $input to be walked; instead, the enumerator object itself is cast to [object[]], which is not the intent.
To put it differently: $input isn't the collection of input objects, it is a helper object for enumerating the collection of input objects.